diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9af84740ad..9a687fb442 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -#### Brief description of the issue +#### Brief description of the bug #### What you expected to happen diff --git a/code/ATMOSPHERICS/components/shutoff.dm b/code/ATMOSPHERICS/components/shutoff.dm new file mode 100644 index 0000000000..de69f5e395 --- /dev/null +++ b/code/ATMOSPHERICS/components/shutoff.dm @@ -0,0 +1,57 @@ +GLOBAL_LIST_EMPTY(shutoff_valves) + +/obj/machinery/atmospherics/valve/shutoff + icon = 'icons/atmos/clamp.dmi' + icon_state = "map_vclamp0" + pipe_state = "vclamp" + + name = "automatic shutoff valve" + desc = "An automatic valve with control circuitry and pipe integrity sensor, capable of automatically isolating damaged segments of the pipe network." + var/close_on_leaks = TRUE // If false it will be always open + level = 1 + +/obj/machinery/atmospherics/valve/shutoff/update_icon() + icon_state = "vclamp[open]" + +/obj/machinery/atmospherics/valve/shutoff/examine(var/mob/user) + ..() + to_chat(user, "The automatic shutoff circuit is [close_on_leaks ? "enabled" : "disabled"].") + +/obj/machinery/atmospherics/valve/shutoff/Initialize() + . = ..() + open() + GLOB.shutoff_valves += src + hide(1) + +/obj/machinery/atmospherics/valve/shutoff/Destroy() + GLOB.shutoff_valves -= src + ..() + +/obj/machinery/atmospherics/valve/shutoff/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/atmospherics/valve/shutoff/attack_hand(var/mob/user) + src.add_fingerprint(usr) + update_icon(1) + close_on_leaks = !close_on_leaks + to_chat(user, "You [close_on_leaks ? "enable" : "disable"] the automatic shutoff circuit.") + return TRUE + +/obj/machinery/atmospherics/valve/shutoff/process() + ..() + + if (!network_node1 || !network_node2) + if(open) + close() + return + + if (!close_on_leaks) + if (!open) + open() + return + + if (network_node1.leaks.len || network_node2.leaks.len) + if (open) + close() + else if (!open) + open() diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index ee1379ac28..5e62fe5802 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -1,4 +1,4 @@ -//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively, +//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively, //but it does not permit gas to flow back from the environment into the injector. Can be turned off to prevent any gas flow. //When it receives the "inject" signal, it will try to pump it's entire contents into the environment regardless of pressure, using power. @@ -13,7 +13,7 @@ use_power = 0 idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 15000 //15000 W ~ 20 HP - + var/injecting = 0 var/volume_rate = 50 //flow rate limit @@ -26,7 +26,7 @@ /obj/machinery/atmospherics/unary/outlet_injector/New() ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. /obj/machinery/atmospherics/unary/outlet_injector/Destroy() unregister_radio(src, frequency) @@ -60,21 +60,21 @@ if((stat & (NOPOWER|BROKEN)) || !use_power) return - + var/power_draw = -1 var/datum/gas_mixture/environment = loc.return_air() - + if(environment && air_contents.temperature > 0) var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - + if (power_draw >= 0) last_power_draw = power_draw use_power(power_draw) - + if(network) network.update = 1 - + return 1 /obj/machinery/atmospherics/unary/outlet_injector/proc/inject() @@ -84,7 +84,7 @@ var/datum/gas_mixture/environment = loc.return_air() if (!environment) return 0 - + injecting = 1 if(air_contents.temperature > 0) @@ -155,4 +155,23 @@ update_icon() /obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) - update_underlays() \ No newline at end of file + update_underlays() + +/obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) + to_chat(user, "You toggle \the [src].") + injecting = !injecting + use_power = injecting + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.is_wrench()) + return ..() + + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index ce2eb97e67..1f7414e17f 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -16,7 +16,7 @@ desc = "Has a valve and pump attached to it" use_power = 0 idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP + power_rating = 30000 //7500 W ~ 10 HP //VOREStation Edit - 30000 W connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes @@ -89,7 +89,7 @@ /obj/machinery/atmospherics/unary/vent_pump/high_volume name = "Large Air Vent" power_channel = EQUIP - power_rating = 15000 //15 kW ~ 20 HP + power_rating = 45000 //15 kW ~ 20 HP //VOREStation Edit - 45000 /obj/machinery/atmospherics/unary/vent_pump/high_volume/New() ..() diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index eb5e89276e..a611e30a89 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -8,76 +8,83 @@ var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SS var/list/datum/pipeline/line_members = list() //membership roster to go through for updates and what not + var/list/leaks = list() + var/update = 1 //var/datum/gas_mixture/air_transient = null - Destroy() - STOP_PROCESSING_PIPENET(src) - for(var/datum/pipeline/line_member in line_members) - line_member.network = null - for(var/obj/machinery/atmospherics/normal_member in normal_members) - normal_member.reassign_network(src, null) - gases.Cut() // Do not qdel the gases, we don't own them - return ..() +/datum/pipe_network/Destroy() + STOP_PROCESSING_PIPENET(src) + for(var/datum/pipeline/line_member in line_members) + line_member.network = null + for(var/obj/machinery/atmospherics/normal_member in normal_members) + normal_member.reassign_network(src, null) + gases.Cut() // Do not qdel the gases, we don't own them + leaks.Cut() + return ..() - process() - //Equalize gases amongst pipe if called for - if(update) - update = 0 - reconcile_air() //equalize_gases(gases) +/datum/pipe_network/process() + //Equalize gases amongst pipe if called for + if(update) + update = 0 + reconcile_air() //equalize_gases(gases) - //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport - //for(var/datum/pipeline/line_member in line_members) - // line_member.process() + listclearnulls(leaks) // Let's not have forever-seals. - proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) - //Purpose: Generate membership roster - //Notes: Assuming that members will add themselves to appropriate roster in network_expand() + //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport + //for(var/datum/pipeline/line_member in line_members) + // line_member.process() - if(!start_normal) - qdel(src) - return +/datum/pipe_network/proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) + //Purpose: Generate membership roster + //Notes: Assuming that members will add themselves to appropriate roster in network_expand() - start_normal.network_expand(src, reference) + if(!start_normal) + qdel(src) + return - update_network_gases() + start_normal.network_expand(src, reference) - if((normal_members.len>0)||(line_members.len>0)) - START_PROCESSING_PIPENET(src) - else - qdel(src) + update_network_gases() - proc/merge(datum/pipe_network/giver) - if(giver==src) return 0 + if((normal_members.len>0)||(line_members.len>0)) + START_PROCESSING_PIPENET(src) + else + qdel(src) - normal_members |= giver.normal_members +/datum/pipe_network/proc/merge(datum/pipe_network/giver) + if(giver==src) return 0 - line_members |= giver.line_members + normal_members |= giver.normal_members - for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) - normal_member.reassign_network(giver, src) + line_members |= giver.line_members - for(var/datum/pipeline/line_member in giver.line_members) - line_member.network = src + leaks |= giver.leaks - update_network_gases() - return 1 + for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) + normal_member.reassign_network(giver, src) - proc/update_network_gases() - //Go through membership roster and make sure gases is up to date + for(var/datum/pipeline/line_member in giver.line_members) + line_member.network = src - gases = list() - volume = 0 + update_network_gases() + return 1 - for(var/obj/machinery/atmospherics/normal_member in normal_members) - var/result = normal_member.return_network_air(src) - if(result) gases += result +/datum/pipe_network/proc/update_network_gases() + //Go through membership roster and make sure gases is up to date - for(var/datum/pipeline/line_member in line_members) - gases += line_member.air + gases = list() + volume = 0 - for(var/datum/gas_mixture/air in gases) - volume += air.volume + for(var/obj/machinery/atmospherics/normal_member in normal_members) + var/result = normal_member.return_network_air(src) + if(result) gases += result - proc/reconcile_air() - equalize_gases(gases) + for(var/datum/pipeline/line_member in line_members) + gases += line_member.air + + for(var/datum/gas_mixture/air in gases) + volume += air.volume + +/datum/pipe_network/proc/reconcile_air() + equalize_gases(gases) diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index fc47bca938..b53d722458 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,219 +1,234 @@ -datum/pipeline +/datum/pipeline var/datum/gas_mixture/air var/list/obj/machinery/atmospherics/pipe/members var/list/obj/machinery/atmospherics/pipe/edges //Used for building networks + // Nodes that are leaking. Used for A.S. Valves. + var/list/leaks = list() + var/datum/pipe_network/network var/alert_pressure = 0 - Destroy() - QDEL_NULL(network) +/datum/pipeline/Destroy() + QDEL_NULL(network) - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - members = null - edges = null - . = ..() + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + members = null + edges = null + leaks = null + . = ..() - process()//This use to be called called from the pipe networks - - //Check to see if pressure is within acceptable limits - var/pressure = air.return_pressure() - if(pressure > alert_pressure) - for(var/obj/machinery/atmospherics/pipe/member in members) - if(!member.check_pressure(pressure)) - break //Only delete 1 pipe per process - - proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio +/datum/pipeline/process()//This use to be called called from the pipe networks + //Check to see if pressure is within acceptable limits + var/pressure = air.return_pressure() + if(pressure > alert_pressure) for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.copy_from(air) - member.air_temporary.volume = member.volume - member.air_temporary.multiply(member.volume / air.volume) + if(!member.check_pressure(pressure)) + break //Only delete 1 pipe per process - proc/build_pipeline(obj/machinery/atmospherics/pipe/base) +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.copy_from(air) + member.air_temporary.volume = member.volume + member.air_temporary.multiply(member.volume / air.volume) + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/pipe/base) + air = new + + var/list/possible_expansions = list(base) + members = list(base) + edges = list() + + var/volume = base.volume + base.parent = src + alert_pressure = base.alert_pressure + + if(base.air_temporary) + air = base.air_temporary + base.air_temporary = null + else air = new - var/list/possible_expansions = list(base) - members = list(base) - edges = list() + if(base.leaking) + leaks |= base - var/volume = base.volume - base.parent = src - alert_pressure = base.alert_pressure + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) - if(base.air_temporary) - air = base.air_temporary - base.air_temporary = null - else - air = new + var/list/result = borderline.pipeline_expansion() + var/edge_check = result.len - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) + if(result.len>0) + for(var/obj/machinery/atmospherics/pipe/item in result) - var/list/result = borderline.pipeline_expansion() - var/edge_check = result.len + if(item.in_stasis) + continue - if(result.len>0) - for(var/obj/machinery/atmospherics/pipe/item in result) - if(!members.Find(item)) - members += item - possible_expansions += item + if(!members.Find(item)) + members += item + possible_expansions += item - volume += item.volume - item.parent = src + volume += item.volume + item.parent = src - alert_pressure = min(alert_pressure, item.alert_pressure) + alert_pressure = min(alert_pressure, item.alert_pressure) - if(item.air_temporary) - air.merge(item.air_temporary) + if(item.air_temporary) + air.merge(item.air_temporary) - edge_check-- + if(item.leaking) + leaks |= item - if(edge_check>0) - edges += borderline + edge_check-- - possible_expansions -= borderline + if(edge_check>0) + edges += borderline - air.volume = volume + possible_expansions -= borderline - proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + air.volume = volume - if(new_network.line_members.Find(src)) - return 0 +/datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - new_network.line_members += src + if(new_network.line_members.Find(src)) + return 0 - network = new_network + new_network.line_members += src - for(var/obj/machinery/atmospherics/pipe/edge in edges) - for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) - if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) - result.network_expand(new_network, edge) + network = new_network + network.leaks |= leaks - return 1 + for(var/obj/machinery/atmospherics/pipe/edge in edges) + for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) + if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) + result.network_expand(new_network, edge) - proc/return_network(obj/machinery/atmospherics/reference) - if(!network) - network = new /datum/pipe_network() - network.build_network(src, null) - //technically passing these parameters should not be allowed - //however pipe_network.build_network(..) and pipeline.network_extend(...) - // were setup to properly handle this case + return 1 - return network +/datum/pipeline/proc/return_network(obj/machinery/atmospherics/reference) + if(!network) + network = new /datum/pipe_network() + network.build_network(src, null) + //technically passing these parameters should not be allowed + //however pipe_network.build_network(..) and pipeline.network_extend(...) + // were setup to properly handle this case - proc/mingle_with_turf(turf/simulated/target, mingle_volume) - var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) - air_sample.volume = mingle_volume + return network - if(istype(target) && target.zone) - //Have to consider preservation of group statuses - var/datum/gas_mixture/turf_copy = new - var/datum/gas_mixture/turf_original = new +/datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) + var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) + air_sample.volume = mingle_volume - turf_copy.copy_from(target.zone.air) - turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group - turf_original.copy_from(turf_copy) + if(istype(target) && target.zone) + //Have to consider preservation of group statuses + var/datum/gas_mixture/turf_copy = new + var/datum/gas_mixture/turf_original = new - equalize_gases(list(air_sample, turf_copy)) - air.merge(air_sample) + turf_copy.copy_from(target.zone.air) + turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group + turf_original.copy_from(turf_copy) + + equalize_gases(list(air_sample, turf_copy)) + air.merge(air_sample) - target.zone.air.remove(turf_original.total_moles) - target.zone.air.merge(turf_copy) + target.zone.air.remove(turf_original.total_moles) + target.zone.air.merge(turf_copy) - else - var/datum/gas_mixture/turf_air = target.return_air() + else + var/datum/gas_mixture/turf_air = target.return_air() - equalize_gases(list(air_sample, turf_air)) - air.merge(air_sample) - //turf_air already modified by equalize_gases() + equalize_gases(list(air_sample, turf_air)) + air.merge(air_sample) + //turf_air already modified by equalize_gases() - if(network) - network.update = 1 + if(network) + network.update = 1 - proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target - if(modeled_location.blocks_air) + if(modeled_location.blocks_air) - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - if(modeled_location.zone) - delta_temperature = (air.temperature - modeled_location.zone.air.temperature) - sharer_heat_capacity = modeled_location.zone.air.heat_capacity() - else - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - if(modeled_location.zone) - modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier - else - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) air.temperature -= heat/total_heat_capacity - if(network) - network.update = 1 + modeled_location.temperature += heat/modeled_location.heat_capacity - //surface must be the surface area in m^2 - proc/radiate_heat_to_space(surface, thermal_conductivity) - var/gas_density = air.total_moles/air.volume - thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 - // We only get heat from the star on the exposed surface area. - // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. - var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity + if(modeled_location.zone) + delta_temperature = (air.temperature - modeled_location.zone.air.temperature) + sharer_heat_capacity = modeled_location.zone.air.heat_capacity() + else + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() - // Previously, the temperature would enter equilibrium at 26C or 294K. - // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. - // It currently should stabilise at 129.6K or -143.6C - heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 - air.add_thermal_energy(heat_gain) - if(network) - network.update = 1 + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + if(modeled_location.zone) + modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier + else + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + if(network) + network.update = 1 + +//surface must be the surface area in m^2 +/datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) + var/gas_density = air.total_moles/air.volume + thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio + + // We only get heat from the star on the exposed surface area. + // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. + var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity + + // Previously, the temperature would enter equilibrium at 26C or 294K. + // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. + // It currently should stabilise at 129.6K or -143.6C + heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 + + air.add_thermal_energy(heat_gain) + if(network) + network.update = 1 diff --git a/code/ATMOSPHERICS/pipes/cap.dm b/code/ATMOSPHERICS/pipes/cap.dm index a03ce16af5..1e32aa6295 100644 --- a/code/ATMOSPHERICS/pipes/cap.dm +++ b/code/ATMOSPHERICS/pipes/cap.dm @@ -54,7 +54,7 @@ alpha = 255 overlays.Cut() - overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, "cap") + overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, "cap[icon_connect_type]") /obj/machinery/atmospherics/pipe/cap/atmos_init() for(var/obj/machinery/atmospherics/target in get_step(src, dir)) diff --git a/code/ATMOSPHERICS/pipes/he_pipes.dm b/code/ATMOSPHERICS/pipes/he_pipes.dm index 2d77e3a4ca..1b283e3ba4 100644 --- a/code/ATMOSPHERICS/pipes/he_pipes.dm +++ b/code/ATMOSPHERICS/pipes/he_pipes.dm @@ -67,8 +67,22 @@ return update_icon() + handle_leaking() return +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/set_leaking(var/new_leaking) // They already process, no need for manual processing toggles. + if(new_leaking && !leaking) + leaking = TRUE + if(parent) + parent.leaks |= src + if(parent.network) + parent.network.leaks |= src + else if (!new_leaking && leaking) + leaking = FALSE + if(parent) + parent.leaks -= src + if(parent.network) + parent.network.leaks -= src /obj/machinery/atmospherics/pipe/simple/heat_exchanging/process() if(!parent) @@ -180,4 +194,5 @@ return update_icon() + handle_leaking() return diff --git a/code/ATMOSPHERICS/pipes/he_pipes_vr.dm b/code/ATMOSPHERICS/pipes/he_pipes_vr.dm new file mode 100644 index 0000000000..d763f75b74 --- /dev/null +++ b/code/ATMOSPHERICS/pipes/he_pipes_vr.dm @@ -0,0 +1,2 @@ +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/set_leaking(var/new_leaking) + return //Nope \ No newline at end of file diff --git a/code/ATMOSPHERICS/pipes/manifold.dm b/code/ATMOSPHERICS/pipes/manifold.dm index 524d420d39..f44bfc5686 100644 --- a/code/ATMOSPHERICS/pipes/manifold.dm +++ b/code/ATMOSPHERICS/pipes/manifold.dm @@ -68,9 +68,24 @@ node3 = null update_icon() + handle_leaking() ..() +/obj/machinery/atmospherics/pipe/manifold/handle_leaking() + if(node1 && node2 && node3) + set_leaking(FALSE) + else + set_leaking(TRUE) + +/obj/machinery/atmospherics/pipe/manifold/process() + if(!parent) + ..() + else if(leaking) + parent.mingle_with_turf(loc, volume) + else + . = PROCESS_KILL + /obj/machinery/atmospherics/pipe/manifold/change_color(var/new_color) ..() //for updating connected atmos device pipes (i.e. vents, manifolds, etc) @@ -154,6 +169,7 @@ var/turf/T = get_turf(src) if(level == 1 && !T.is_plating()) hide(1) update_icon() + handle_leaking() /obj/machinery/atmospherics/pipe/manifold/visible icon_state = "map" diff --git a/code/ATMOSPHERICS/pipes/manifold4w.dm b/code/ATMOSPHERICS/pipes/manifold4w.dm index 0cc022423b..197c1a090b 100644 --- a/code/ATMOSPHERICS/pipes/manifold4w.dm +++ b/code/ATMOSPHERICS/pipes/manifold4w.dm @@ -66,9 +66,24 @@ node4 = null update_icon() + handle_leaking() ..() +/obj/machinery/atmospherics/pipe/manifold4w/handle_leaking() + if(node1 && node2 && node3 && node4) + set_leaking(FALSE) + else + set_leaking(TRUE) + +/obj/machinery/atmospherics/pipe/manifold4w/process() + if(!parent) + ..() + else if(leaking) + parent.mingle_with_turf(loc, volume) + else + . = PROCESS_KILL + /obj/machinery/atmospherics/pipe/manifold4w/change_color(var/new_color) ..() //for updating connected atmos device pipes (i.e. vents, manifolds, etc) @@ -156,6 +171,7 @@ var/turf/T = get_turf(src) if(level == 1 && !T.is_plating()) hide(1) update_icon() + handle_leaking() /obj/machinery/atmospherics/pipe/manifold4w/visible icon_state = "map_4way" diff --git a/code/ATMOSPHERICS/pipes/pipe_base.dm b/code/ATMOSPHERICS/pipes/pipe_base.dm index a035857e54..d1bb92fac7 100644 --- a/code/ATMOSPHERICS/pipes/pipe_base.dm +++ b/code/ATMOSPHERICS/pipes/pipe_base.dm @@ -6,6 +6,7 @@ var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke var/datum/pipeline/parent var/volume = 0 + var/leaking = FALSE // Do not set directly, use set_leaking(TRUE/FALSE) layer = PIPES_LAYER use_power = 0 @@ -13,6 +14,7 @@ pipe_flags = 0 // Does not have PIPING_DEFAULT_LAYER_ONLY flag. var/alert_pressure = 80*ONE_ATMOSPHERE + var/in_stasis = FALSE //minimum pressure before check_pressure(...) should be called can_buckle = 1 @@ -30,6 +32,31 @@ /obj/machinery/atmospherics/pipe/hides_under_flooring() return level != 2 +/obj/machinery/atmospherics/pipe/proc/set_leaking(var/new_leaking) + if(new_leaking && !leaking) + if(!speed_process) + START_MACHINE_PROCESSING(src) + else + START_PROCESSING(SSfastprocess, src) + leaking = TRUE + if(parent) + parent.leaks |= src + if(parent.network) + parent.network.leaks |= src + else if (!new_leaking && leaking) + if(!speed_process) + STOP_MACHINE_PROCESSING(src) + else + STOP_PROCESSING(SSfastprocess, src) + leaking = FALSE + if(parent) + parent.leaks -= src + if(parent.network) + parent.network.leaks -= src + +/obj/machinery/atmospherics/pipe/proc/handle_leaking() // Used specifically to update leaking status on different pipes. + return + /obj/machinery/atmospherics/pipe/proc/pipeline_expansion() return null diff --git a/code/ATMOSPHERICS/pipes/pipe_base_vr.dm b/code/ATMOSPHERICS/pipes/pipe_base_vr.dm new file mode 100644 index 0000000000..d20e73ae89 --- /dev/null +++ b/code/ATMOSPHERICS/pipes/pipe_base_vr.dm @@ -0,0 +1,2 @@ +/obj/machinery/atmospherics/pipe/set_leaking(var/new_leaking) + return // N O P E \ No newline at end of file diff --git a/code/ATMOSPHERICS/pipes/simple.dm b/code/ATMOSPHERICS/pipes/simple.dm index 6aee81807e..eb980c3b47 100644 --- a/code/ATMOSPHERICS/pipes/simple.dm +++ b/code/ATMOSPHERICS/pipes/simple.dm @@ -1,6 +1,6 @@ // // Simple Pipes - Just a tube, maybe bent -// +// /obj/machinery/atmospherics/pipe/simple icon = 'icons/atmos/pipes.dmi' icon_state = "" @@ -34,6 +34,14 @@ icon = null alpha = 255 +/obj/machinery/atmospherics/pipe/simple/process() + if(!parent) + ..() + else if(leaking) + parent.mingle_with_turf(loc, volume) + else + . = PROCESS_KILL + /obj/machinery/atmospherics/pipe/simple/check_pressure(pressure) var/datum/gas_mixture/environment = loc.return_air() @@ -147,6 +155,7 @@ var/turf/T = loc if(level == 1 && !T.is_plating()) hide(1) update_icon() + handle_leaking() /obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/reference) if(reference == node1) @@ -160,9 +169,16 @@ node2 = null update_icon() + handle_leaking() return null +/obj/machinery/atmospherics/pipe/simple/handle_leaking() + if(node1 && node2) + set_leaking(FALSE) + else + set_leaking(TRUE) + /obj/machinery/atmospherics/pipe/simple/visible icon_state = "intact" level = 2 diff --git a/code/ATMOSPHERICS/pipes/universal.dm b/code/ATMOSPHERICS/pipes/universal.dm index 2d8bd09dff..00d2746947 100644 --- a/code/ATMOSPHERICS/pipes/universal.dm +++ b/code/ATMOSPHERICS/pipes/universal.dm @@ -48,7 +48,7 @@ construction_type = /obj/item/pipe/binary pipe_state = "universal" -/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0) +/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0) // Doesn't leak. It's a special pipe. if(!check_icon_cache()) return diff --git a/code/ZAS/Phoron.dm b/code/ZAS/Phoron.dm index b7ab1999f0..6ffec0be99 100644 --- a/code/ZAS/Phoron.dm +++ b/code/ZAS/Phoron.dm @@ -47,6 +47,12 @@ obj/var/contaminated = 0 return 0 else if(istype(src,/obj/item/weapon/storage/backpack)) return 0 //Cannot be washed :( + //VOREStation Addition start + else if(isbelly(loc)) + return 0 + else if(ismob(loc) && isbelly(loc.loc)) + return 0 + //VOREStation Addition end else if(istype(src,/obj/item/clothing)) return 1 diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 3bee841881..c54fbe93cc 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -145,6 +145,7 @@ What is the naming convention for planes or layers? #define LAYER_HUD_ITEM 3 //Things sitting on HUD items (largely irrelevant because PLANE_PLAYER_HUD_ITEMS) #define LAYER_HUD_ABOVE 4 //Things that reside above items (highlights) #define PLANE_PLAYER_HUD_ITEMS 96 //Separate layer with which to apply colorblindness +#define PLANE_PLAYER_HUD_ABOVE 97 //Things above the player hud #define PLANE_ADMIN3 99 //Purely for shenanigans (above HUD) diff --git a/code/__defines/belly_modes_vr.dm b/code/__defines/belly_modes_vr.dm index c65a87d6c8..ccffcf4341 100644 --- a/code/__defines/belly_modes_vr.dm +++ b/code/__defines/belly_modes_vr.dm @@ -32,6 +32,7 @@ #define DM_FLAG_NUMBING 0x1 #define DM_FLAG_STRIPPING 0x2 #define DM_FLAG_LEAVEREMAINS 0x4 +#define DM_FLAG_THICKBELLY 0x8 //Item related modes #define IM_HOLD "Hold" diff --git a/code/__defines/chemistry.dm b/code/__defines/chemistry.dm index 399c7e879b..0976ed5ea3 100644 --- a/code/__defines/chemistry.dm +++ b/code/__defines/chemistry.dm @@ -35,6 +35,7 @@ #define CE_ALCOHOL_TOXIC "alcotoxic" // Liver damage #define CE_SPEEDBOOST "gofast" // Hyperzine #define CE_SLOWDOWN "goslow" // Slowdown +#define CE_ANTACID "nopuke" // Don't puke. #define REAGENTS_PER_SHEET 20 diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 285b0b341e..c87b9942df 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -8,6 +8,7 @@ #define HALLOSS "halloss" #define ELECTROCUTE "electrocute" #define BIOACID "bioacid" +#define SEARING "searing" #define CUT "cut" #define BRUISE "bruise" @@ -47,6 +48,7 @@ // Damage above this value must be repaired with surgery. #define ROBOLIMB_REPAIR_CAP 30 +#define ORGAN_FLESH 0 // Normal organic organs. #define ORGAN_ASSISTED 1 // Like pacemakers, not robotic #define ORGAN_ROBOT 2 // Fully robotic, no organic parts #define ORGAN_LIFELIKE 3 // Robotic, made to appear organic diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm index 64782715d3..e59dac70c5 100644 --- a/code/__defines/flags.dm +++ b/code/__defines/flags.dm @@ -38,6 +38,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define NOSLIP (1<<2) // Prevents from slipping on wet floors, in space, etc. #define BLOCK_GAS_SMOKE_EFFECT (1<<3) // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL) #define FLEXIBLEMATERIAL (1<<4) // At the moment, masks with this flag will not prevent eating even if they are covering your face. +#define ALLOW_SURVIVALFOOD (1<<5) // Allows special survival food items to be eaten through it // Flags for pass_flags. - Used in /atom/var/pass_flags #define PASSTABLE (1<<0) diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index bf8f5129f5..0425c3fa63 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -16,6 +16,8 @@ #define LIGHTING_SOFT_THRESHOLD 0.05 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. This also should be the transparancy of the "soft_dark" icon state. +#define LIGHTING_MULT_FACTOR 0.5 + // If I were you I'd leave this alone. #define LIGHTING_BASE_MATRIX \ list \ @@ -77,7 +79,7 @@ //Lighting values used by the station lights #define LIGHT_COLOR_FLUORESCENT_TUBE "#E0EFFF" #define LIGHT_COLOR_FLUORESCENT_FLASHLIGHT "#CDDDFF" -#define LIGHT_COLOR_INCANDESCENT_TUBE "#FFEEDD" +#define LIGHT_COLOR_INCANDESCENT_TUBE "#FFFEB8" #define LIGHT_COLOR_INCANDESCENT_BULB "#FFDDBB" #define LIGHT_COLOR_INCANDESCENT_FLASHLIGHT "#FFCC66" diff --git a/code/__defines/lighting_vr.dm b/code/__defines/lighting_vr.dm new file mode 100644 index 0000000000..0cd6b060a1 --- /dev/null +++ b/code/__defines/lighting_vr.dm @@ -0,0 +1,2 @@ +#define LIGHT_COLOR_INCANDESCENT_TUBE "#E0EFF0" +#define LIGHT_COLOR_INCANDESCENT_BULB "#FFFEB8" \ No newline at end of file diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 95d4929acb..43937e0cbe 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -34,6 +34,7 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define NETWORK_CRESCENT "Spaceport" // #define NETWORK_CAFE_DOCK "Cafe Dock" #define NETWORK_CARGO "Cargo" +#define NETWORK_CIRCUITS "Circuits" #define NETWORK_CIVILIAN "Civilian" // #define NETWORK_CIVILIAN_EAST "Civilian East" // #define NETWORK_CIVILIAN_WEST "Civilian West" @@ -54,8 +55,11 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define NETWORK_SECURITY "Security" #define NETWORK_INTERROGATION "Interrogation" #define NETWORK_TELECOM "Tcomms" -#define NETWORK_THUNDER "Thunderdome" +#define NETWORK_THUNDER "Entertainment" //VOREStation Edit: broader definition #define NETWORK_COMMUNICATORS "Communicators" +#define NETWORK_ALARM_ATMOS "Atmosphere Alarms" +#define NETWORK_ALARM_POWER "Power Alarms" +#define NETWORK_ALARM_FIRE "Fire Alarms" // Those networks can only be accessed by pre-existing terminals. AIs and new terminals can't use them. var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret", NETWORK_COMMUNICATORS) @@ -69,14 +73,6 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret #define STAGE_FIVE 9 #define STAGE_SUPER 11 -// computer3 error codes, move lower in the file when it passes dev -Sayu -#define PROG_CRASH 0x1 // Generic crash. -#define MISSING_PERIPHERAL 0x2 // Missing hardware. -#define BUSTED_ASS_COMPUTER 0x4 // Self-perpetuating error. BAC will continue to crash forever. -#define MISSING_PROGRAM 0x8 // Some files try to automatically launch a program. This is that failing. -#define FILE_DRM 0x10 // Some files want to not be copied/moved. This is them complaining that you tried. -#define NETWORK_FAILURE 0x20 - // NanoUI flags #define STATUS_INTERACTIVE 2 // GREEN Visability #define STATUS_UPDATE 1 // ORANGE Visability @@ -106,6 +102,16 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret #define ATMOS_DEFAULT_VOLUME_MIXER 200 // L. #define ATMOS_DEFAULT_VOLUME_PIPE 70 // L. +// These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse! +#define SUPERMATTER_ERROR -1 // Unknown status, shouldn't happen but just in case. +#define SUPERMATTER_INACTIVE 0 // No or minimal energy +#define SUPERMATTER_NORMAL 1 // Normal operation +#define SUPERMATTER_NOTIFY 2 // Ambient temp > 80% of CRITICAL_TEMPERATURE +#define SUPERMATTER_WARNING 3 // Ambient temp > CRITICAL_TEMPERATURE OR integrity damaged +#define SUPERMATTER_DANGER 4 // Integrity < 50% +#define SUPERMATTER_EMERGENCY 5 // Integrity < 25% +#define SUPERMATTER_DELAMINATING 6 // Pretty obvious. + //wIP - PORT ALL OF THESE TO SUBSYSTEMS AND GET RID OF THE WHOLE LIST PROCESS THING // Fancy-pants START/STOP_PROCESSING() macros that lets us custom define what the list is. #define START_PROCESSING_IN_LIST(DATUM, LIST) \ diff --git a/code/__defines/map.dm b/code/__defines/map.dm index 7d0f3706af..fce63b9955 100644 --- a/code/__defines/map.dm +++ b/code/__defines/map.dm @@ -9,4 +9,4 @@ #define MAP_LEVEL_XENOARCH_EXEMPT 0x080 // Z-levels exempt from xenoarch digsite generation. // Misc map defines. -#define SUBMAP_MAP_EDGE_PAD 15 // Automatically created submaps are forbidden from being this close to the main map's edge. \ No newline at end of file +#define SUBMAP_MAP_EDGE_PAD 8 // Automatically created submaps are forbidden from being this close to the main map's edge. //VOREStation Edit \ No newline at end of file diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index c17e6d1594..69b91d0dc3 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -113,6 +113,7 @@ #define MAX_RECORD_LENGTH 24576 #define MAX_LNAME_LEN 64 #define MAX_NAME_LEN 52 +#define MAX_TEXTFILE_LENGTH 128000 // 512GQ file // Event defines. #define EVENT_LEVEL_MUNDANE 1 @@ -174,6 +175,7 @@ #define MAT_LEAD "lead" #define MAT_SUPERMATTER "supermatter" #define MAT_METALHYDROGEN "mhydrogen" +#define MAT_OSMIUM "osmium" #define SHARD_SHARD "shard" #define SHARD_SHRAPNEL "shrapnel" @@ -191,6 +193,33 @@ #define BOMBCAP_HEAVY_RADIUS (max_explosion_range/2) #define BOMBCAP_LIGHT_RADIUS max_explosion_range #define BOMBCAP_FLASH_RADIUS (max_explosion_range*1.5) + // NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc) +#define NTNET_SOFTWAREDOWNLOAD 1 // Downloads of software from NTNet +#define NTNET_PEERTOPEER 2 // P2P transfers of files between devices +#define NTNET_COMMUNICATION 3 // Communication (messaging) +#define NTNET_SYSTEMCONTROL 4 // Control of various systems, RCon, air alarm control, etc. + +// NTNet transfer speeds, used when downloading/uploading a file/program. +#define NTNETSPEED_LOWSIGNAL 0.25 // GQ/s transfer speed when the device is wirelessly connected and on Low signal +#define NTNETSPEED_HIGHSIGNAL 0.5 // GQ/s transfer speed when the device is wirelessly connected and on High signal +#define NTNETSPEED_ETHERNET 1.0 // GQ/s transfer speed when the device is using wired connection +#define NTNETSPEED_DOS_AMPLIFICATION 5 // Multiplier for Denial of Service program. Resulting load on NTNet relay is this multiplied by NTNETSPEED of the device + +// Program bitflags +#define PROGRAM_ALL 15 +#define PROGRAM_CONSOLE 1 +#define PROGRAM_LAPTOP 2 +#define PROGRAM_TABLET 4 +#define PROGRAM_TELESCREEN 8 + +#define PROGRAM_STATE_KILLED 0 +#define PROGRAM_STATE_BACKGROUND 1 +#define PROGRAM_STATE_ACTIVE 2 + +// Caps for NTNet logging. Less than 10 would make logging useless anyway, more than 500 may make the log browser too laggy. Defaults to 100 unless user changes it. +#define MAX_NTNET_LOGS 500 +#define MIN_NTNET_LOGS 10 + // Special return values from bullet_act(). Positive return values are already used to indicate the blocked level of the projectile. #define PROJECTILE_CONTINUE -1 //if the projectile should continue flying after calling bullet_act() @@ -319,7 +348,14 @@ var/global/list/##LIST_NAME = list();\ #define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT // Radiation 'levels'. Used for the geiger counter, for visuals and sound. They are in different files so this goes here. -#define RAD_LEVEL_LOW 0.01 // Around the level at which radiation starts to become harmful -#define RAD_LEVEL_MODERATE 10 +#define RAD_LEVEL_LOW 0.5 // Around the level at which radiation starts to become harmful +#define RAD_LEVEL_MODERATE 5 #define RAD_LEVEL_HIGH 25 -#define RAD_LEVEL_VERY_HIGH 50 +#define RAD_LEVEL_VERY_HIGH 75 + +#define RADIATION_THRESHOLD_CUTOFF 0.1 // Radiation will not affect a tile when below this value. + +//https://secure.byond.com/docs/ref/info.html#/atom/var/mouse_opacity +#define MOUSE_OPACITY_TRANSPARENT 0 +#define MOUSE_OPACITY_ICON 1 +#define MOUSE_OPACITY_OPAQUE 2 diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 51039af253..de9ffa6cf9 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -194,7 +194,24 @@ #define O_KIDNEYS "kidneys" #define O_APPENDIX "appendix" #define O_VOICE "voicebox" -#define O_STANDARD list(O_EYES, O_HEART, O_LUNGS, O_BRAIN, O_LIVER, O_KIDNEYS, O_APPENDIX, O_VOICE) +#define O_SPLEEN "spleen" +#define O_STOMACH "stomach" +#define O_INTESTINE "intestine" +#define O_STANDARD list(O_EYES, O_HEART, O_LUNGS, O_BRAIN, O_LIVER, O_KIDNEYS, O_SPLEEN, O_APPENDIX, O_VOICE, O_STOMACH, O_INTESTINE) + +// Augments +#define O_AUG_EYES "occular augment" + +#define O_AUG_L_FOREARM "left forearm augment" +#define O_AUG_R_FOREARM "right forearm augment" +#define O_AUG_L_UPPERARM "left upperarm augment" +#define O_AUG_R_UPPERARM "right upperarm augment" +#define O_AUG_L_HAND "left hand augment" +#define O_AUG_R_HAND "right hand augment" + +#define O_AUG_RIBS "rib augment" +#define O_AUG_SPINE "spinal augment" +#define O_AUG_PELVIC "pelvic augment" // Non-Standard organs #define O_MOUTH "mouth" @@ -207,13 +224,17 @@ #define O_GBLADDER "gas bladder" #define O_POLYP "polyp segment" #define O_ANCHOR "anchoring ligament" +#define O_REGBRUTE "pneumoregenitor" +#define O_REGBURN "thermoregenitor" +#define O_REGOXY "respiroregenitor" +#define O_REGTOX "toxoregenitor" #define O_ACID "acid gland" #define O_EGG "egg sac" #define O_RESIN "resin spinner" #define O_AREJECT "immune hub" #define O_VENTC "morphoplastic node" #define O_VRLINK "virtual node" -#define O_ALL list(O_STANDARD, O_MOUTH, O_CELL, O_PLASMA, O_HIVE, O_NUTRIENT, O_STRATA, O_RESPONSE, O_GBLADDER, O_POLYP, O_ANCHOR, O_ACID, O_EGG, O_RESIN, O_AREJECT, O_VENTC, O_VRLINK) +#define O_ALL list(O_STANDARD, O_MOUTH, O_CELL, O_PLASMA, O_HIVE, O_NUTRIENT, O_STRATA, O_RESPONSE, O_GBLADDER, O_POLYP, O_ANCHOR, O_REGBRUTE, O_REGBURN, O_REGOXY, O_REGTOX, O_ACID, O_EGG, O_RESIN, O_AREJECT, O_VENTC, O_VRLINK) // External organs, aka limbs #define BP_L_FOOT "l_foot" diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm index 70a8e4317c..b9833c42c0 100644 --- a/code/__defines/mobs_vr.dm +++ b/code/__defines/mobs_vr.dm @@ -36,10 +36,12 @@ #define SPECIES_MONKEY_SERGAL "Saru" #define SPECIES_MONKEY_VULPKANIN "Wolpin" +#define SPECIES_WEREBEAST "Werebeast" #define SPECIES_SHADEKIN "Shadekin" #define SPECIES_SHADEKIN_BLUE "Blue-eyed Shadekin" #define SPECIES_SHADEKIN_RED "Red-eyed Shadekin" #define SPECIES_SHADEKIN_YELLOW "Yellow-eyed Shadekin" #define SPECIES_SHADEKIN_PURPLE "Purple-eyed Shadekin" #define SPECIES_SHADEKIN_ORANGE "Orange-eyed Shadekin" -#define SPECIES_SHADEKIN_GREEN "Green-eyed Shadekin" \ No newline at end of file +#define SPECIES_SHADEKIN_GREEN "Green-eyed Shadekin" +#define SPECIES_WEREBEAST "Werebeast" diff --git a/code/__defines/sound.dm b/code/__defines/sound.dm index 0995f0e071..d954d34b2a 100644 --- a/code/__defines/sound.dm +++ b/code/__defines/sound.dm @@ -8,11 +8,12 @@ #define CHANNEL_AMBIENCE 1018 #define CHANNEL_BUZZ 1017 #define CHANNEL_BICYCLE 1016 +#define CHANNEL_PREYLOOP 1015 //VORESTATION ADD - Fancy Sound Loop channel //THIS SHOULD ALWAYS BE THE LOWEST ONE! //KEEP IT UPDATED -#define CHANNEL_HIGHEST_AVAILABLE 1015 +#define CHANNEL_HIGHEST_AVAILABLE 1014 //VORESTATION EDIT - Fancy Sound Loop channel from 1015 #define SOUND_MINIMUM_PRESSURE 10 #define FALLOFF_SOUNDS 0.5 diff --git a/code/__defines/species_languages_vr.dm b/code/__defines/species_languages_vr.dm index 1004c1e22c..831df5253d 100644 --- a/code/__defines/species_languages_vr.dm +++ b/code/__defines/species_languages_vr.dm @@ -1,5 +1,6 @@ #define SPECIES_WHITELIST_SELECTABLE 0x20 // Can select and customize, but not join as +#define LANGUAGE_SLAVIC "Pan-Slavic" #define LANGUAGE_BIRDSONG "Birdsong" #define LANGUAGE_SAGARU "Sagaru" #define LANGUAGE_CANILUNZT "Canilunzt" diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 767941d58f..fe01b602fc 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -78,6 +78,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_VOTE 9 #define FIRE_PRIORITY_AI 10 #define FIRE_PRIORITY_GARBAGE 15 +#define FIRE_PRIORITY_CHARSETUP 25 #define FIRE_PRIORITY_AIRFLOW 30 #define FIRE_PRIORITY_AIR 35 #define FIRE_PRIORITY_OBJ 40 diff --git a/code/__defines/xenoarcheaology.dm b/code/__defines/xenoarcheaology.dm index 45e40cfbde..8a4988b78c 100644 --- a/code/__defines/xenoarcheaology.dm +++ b/code/__defines/xenoarcheaology.dm @@ -36,7 +36,8 @@ #define ARCHAEO_ALIEN_ITEM 36 #define ARCHAEO_ALIEN_BOAT 37 #define ARCHAEO_IMPERION_CIRCUIT 38 -#define MAX_ARCHAEO 38 +#define ARCHAEO_TELECUBE 39 +#define MAX_ARCHAEO 39 #define DIGSITE_GARDEN 1 #define DIGSITE_ANIMAL 2 diff --git a/code/_global_vars/mobs.dm b/code/_global_vars/mobs.dm index 10d829904a..7e60dc71ea 100644 --- a/code/_global_vars/mobs.dm +++ b/code/_global_vars/mobs.dm @@ -5,3 +5,4 @@ GLOBAL_LIST_EMPTY(stealthminID) GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client GLOBAL_LIST_EMPTY(clients) GLOBAL_LIST_EMPTY(players_by_zlevel) +GLOBAL_LIST_EMPTY(round_text_log) diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm index 553984330d..1eb8ac5456 100644 --- a/code/_helpers/_lists.dm +++ b/code/_helpers/_lists.dm @@ -756,3 +756,10 @@ proc/dd_sortedTextList(list/incoming) . = list() for(var/i in L) . |= i + +#define listequal(A, B) (A.len == B.len && !length(A^B)) + +/proc/popleft(list/L) + if(L.len) + . = L[1] + L.Cut(1,2) diff --git a/code/_helpers/atom_movables.dm b/code/_helpers/atom_movables.dm new file mode 100644 index 0000000000..b838f868e6 --- /dev/null +++ b/code/_helpers/atom_movables.dm @@ -0,0 +1,43 @@ +/proc/get_turf_pixel(atom/movable/AM) + if(!istype(AM)) + return + + //Find AM's matrix so we can use it's X/Y pixel shifts + var/matrix/M = matrix(AM.transform) + + var/pixel_x_offset = AM.pixel_x + M.get_x_shift() + var/pixel_y_offset = AM.pixel_y + M.get_y_shift() + + //Irregular objects + if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size) + var/icon/AMicon = icon(AM.icon, AM.icon_state) + pixel_x_offset += ((AMicon.Width()/world.icon_size)-1)*(world.icon_size*0.5) + pixel_y_offset += ((AMicon.Height()/world.icon_size)-1)*(world.icon_size*0.5) + qdel(AMicon) + + //DY and DX + var/rough_x = round(round(pixel_x_offset,world.icon_size)/world.icon_size) + var/rough_y = round(round(pixel_y_offset,world.icon_size)/world.icon_size) + + //Find coordinates + var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom + var/final_x = T.x + rough_x + var/final_y = T.y + rough_y + + if(final_x || final_y) + return locate(final_x, final_y, T.z) + +// Walks up the loc tree until it finds a holder of the given holder_type +/proc/get_holder_of_type(atom/A, holder_type) + if(!istype(A)) return + for(A, A && !istype(A, holder_type), A=A.loc); + return A + +/atom/movable/proc/throw_at_random(var/include_own_turf, var/maxrange, var/speed) + var/list/turfs = trange(maxrange, src) + if(!maxrange) + maxrange = 1 + + if(!include_own_turf) + turfs -= get_turf(src) + src.throw_at(pick(turfs), maxrange, speed, src) diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index 7d89489474..5da656e95d 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -11,6 +11,8 @@ var/global/list/positive_traits = list() // Positive custom species traits, inde var/global/list/traits_costs = list() // Just path = cost list, saves time in char setup var/global/list/all_traits = list() // All of 'em at once (same instances) +var/global/list/sensorpreflist = list("Off", "Binary", "Vitals", "Tracking", "No Preference") //TFF 5/8/19 - Suit Sensors global list + var/global/list/custom_species_bases = list() // Species that can be used for a Custom Species icon base //stores numeric player size options indexed by name @@ -40,40 +42,8 @@ var/global/list/item_vore_blacklist = list( /obj/item/weapon/disk/nuclear, /obj/item/clothing/suit/storage/hooded/wintercoat/roiz) -var/global/list/digestion_sounds = list( - 'sound/vore/digest1.ogg', - 'sound/vore/digest2.ogg', - 'sound/vore/digest3.ogg', - 'sound/vore/digest4.ogg', - 'sound/vore/digest5.ogg', - 'sound/vore/digest6.ogg', - 'sound/vore/digest7.ogg', - 'sound/vore/digest8.ogg', - 'sound/vore/digest9.ogg', - 'sound/vore/digest10.ogg', - 'sound/vore/digest11.ogg', - 'sound/vore/digest12.ogg') - -var/global/list/death_sounds = list( - 'sound/vore/death1.ogg', - 'sound/vore/death2.ogg', - 'sound/vore/death3.ogg', - 'sound/vore/death4.ogg', - 'sound/vore/death5.ogg', - 'sound/vore/death6.ogg', - 'sound/vore/death7.ogg', - 'sound/vore/death8.ogg', - 'sound/vore/death9.ogg', - 'sound/vore/death10.ogg') - -var/global/list/hunger_sounds = list( - 'sound/vore/growl1.ogg', - 'sound/vore/growl2.ogg', - 'sound/vore/growl3.ogg', - 'sound/vore/growl4.ogg', - 'sound/vore/growl5.ogg') - -var/global/list/vore_sounds = list( +//Classic Vore sounds +var/global/list/classic_vore_sounds = list( "Gulp" = 'sound/vore/gulp.ogg', "Insert" = 'sound/vore/insert.ogg', "Insertion1" = 'sound/vore/insertion1.ogg', @@ -84,15 +54,55 @@ var/global/list/vore_sounds = list( "Squish2" = 'sound/vore/squish2.ogg', "Squish3" = 'sound/vore/squish3.ogg', "Squish4" = 'sound/vore/squish4.ogg', - "Rustle (cloth)" = 'sound/effects/rustle5.ogg', + "Rustle (cloth)" = 'sound/effects/rustle1.ogg', + "Rustle 2 (cloth)" = 'sound/effects/rustle2.ogg', + "Rustle 3 (cloth)" = 'sound/effects/rustle3.ogg', + "Rustle 4 (cloth)" = 'sound/effects/rustle4.ogg', + "Rustle 5 (cloth)" = 'sound/effects/rustle5.ogg', "None" = null) -var/global/list/struggle_sounds = list( - "Squish1" = 'sound/vore/squish1.ogg', - "Squish2" = 'sound/vore/squish2.ogg', - "Squish3" = 'sound/vore/squish3.ogg', - "Squish4" = 'sound/vore/squish4.ogg') +var/global/list/classic_release_sounds = list( + "Rustle (cloth)" = 'sound/effects/rustle1.ogg', + "Rustle 2 (cloth)" = 'sound/effects/rustle2.ogg', + "Rustle 3 (cloth)" = 'sound/effects/rustle3.ogg', + "Rustle 4 (cloth)" = 'sound/effects/rustle4.ogg', + "Rustle 5 (cloth)" = 'sound/effects/rustle5.ogg', + "Splatter" = 'sound/effects/splat.ogg', + "None" = null + ) +//Poojy's Fancy Sounds +var/global/list/fancy_vore_sounds = list( + "Gulp" = 'sound/vore/sunesound/pred/swallow_01.ogg', + "Swallow" = 'sound/vore/sunesound/pred/swallow_02.ogg', + "Insertion1" = 'sound/vore/sunesound/pred/insertion_01.ogg', + "Insertion2" = 'sound/vore/sunesound/pred/insertion_02.ogg', + "Tauric Swallow" = 'sound/vore/sunesound/pred/taurswallow.ogg', + "Stomach Move" = 'sound/vore/sunesound/pred/stomachmove.ogg', + "Schlorp" = 'sound/vore/sunesound/pred/schlorp.ogg', + "Squish1" = 'sound/vore/sunesound/pred/squish_01.ogg', + "Squish2" = 'sound/vore/sunesound/pred/squish_02.ogg', + "Squish3" = 'sound/vore/sunesound/pred/squish_03.ogg', + "Squish4" = 'sound/vore/sunesound/pred/squish_04.ogg', + "Rustle (cloth)" = 'sound/effects/rustle1.ogg', + "Rustle 2 (cloth)" = 'sound/effects/rustle2.ogg', + "Rustle 3 (cloth)" = 'sound/effects/rustle3.ogg', + "Rustle 4 (cloth)" = 'sound/effects/rustle4.ogg', + "Rustle 5 (cloth)" = 'sound/effects/rustle5.ogg', + "None" = null + ) + +var/global/list/fancy_release_sounds = list( + "Rustle (cloth)" = 'sound/effects/rustle1.ogg', + "Rustle 2 (cloth)" = 'sound/effects/rustle2.ogg', + "Rustle 3 (cloth)" = 'sound/effects/rustle3.ogg', + "Rustle 4 (cloth)" = 'sound/effects/rustle4.ogg', + "Rustle 5 (cloth)" = 'sound/effects/rustle5.ogg', + "Stomach Move" = 'sound/vore/sunesound/pred/stomachmove.ogg', + "Pred Escape" = 'sound/vore/sunesound/pred/escape.ogg', + "Splatter" = 'sound/effects/splat.ogg', + "None" = null + ) var/global/list/global_vore_egg_types = list( "Unathi" = UNATHI_EGG, @@ -121,6 +131,8 @@ var/global/list/tf_vore_egg_types = list( "Xenomorph" = /obj/structure/closet/secure_closet/egg/xenomorph) var/global/list/edible_trash = list(/obj/item/broken_device, + /obj/item/clothing/accessory/collar, //TFF 10/7/19 - add option to nom collars, + /obj/item/device/communicator, //TFF 19/9/19 - add option to nom communicators and commwatches, /obj/item/clothing/mask, /obj/item/clothing/glasses, /obj/item/clothing/gloves, diff --git a/code/_helpers/icons_vr.dm b/code/_helpers/icons_vr.dm index be5de68e82..60765578e7 100644 --- a/code/_helpers/icons_vr.dm +++ b/code/_helpers/icons_vr.dm @@ -42,4 +42,18 @@ for(var/x_pixel = 1 to I.Width()) if (I.GetPixel(x_pixel, y_pixel)) return y_pixel - 1 - return null \ No newline at end of file + return null + +//Standard behaviour is to cut pixels from the main icon that are covered by pixels from the mask icon unless passed mask_ready, see below. +/proc/get_icon_difference(var/icon/main, var/icon/mask, var/mask_ready) + /*You should skip prep if the mask is already sprited properly. This significantly improves performance by eliminating most of the realtime icon work. + e.g. A 'ready' mask is a mask where the part you want cut out is missing (no pixels, 0 alpha) from the sprite, and everything else is solid white.*/ + + if(istype(main) && istype(mask)) + if(!mask_ready) //Prep the mask if we're using a regular old sprite and not a special-made mask. + mask.Blend(rgb(255,255,255), ICON_SUBTRACT) //Make all pixels on the mask as black as possible. + mask.Opaque(rgb(255,255,255)) //Make the transparent pixels (background) white. + mask.BecomeAlphaMask() //Make all the black pixels vanish (fully transparent), leaving only the white background pixels. + + main.AddAlphaMask(mask) //Make the pixels in the main icon that are in the transparent zone of the mask icon also vanish (fully transparent). + return main diff --git a/code/_helpers/lists.dm~1fb83e6... Merge pull request #5959 from elgeonmb_suit++ b/code/_helpers/lists.dm~1fb83e6... Merge pull request #5959 from elgeonmb_suit++ deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index c771de02ef..51c9c01527 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -61,8 +61,8 @@ /proc/log_access_in(client/new_client) if (config.log_access) - var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" - WRITE_LOG(diary, "ACCESS IN: [message]") + var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" + WRITE_LOG(diary, "ACCESS IN: [message]") //VOREStation Edit /proc/log_access_out(mob/last_mob) if (config.log_access) @@ -73,26 +73,46 @@ if (config.log_say) WRITE_LOG(diary, "SAY: [speaker.simple_info_line()]: [html_decode(text)]") + //Log the message to in-game dialogue logs, as well. + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + /proc/log_ooc(text, client/user) if (config.log_ooc) WRITE_LOG(diary, "OOC: [user.simple_info_line()]: [html_decode(text)]") + GLOB.round_text_log += "([time_stamp()]) ([user]) OOC: - [text]" + /proc/log_aooc(text, client/user) if (config.log_ooc) WRITE_LOG(diary, "AOOC: [user.simple_info_line()]: [html_decode(text)]") + GLOB.round_text_log += "([time_stamp()]) ([user]) AOOC: - [text]" + /proc/log_looc(text, client/user) if (config.log_ooc) WRITE_LOG(diary, "LOOC: [user.simple_info_line()]: [html_decode(text)]") + GLOB.round_text_log += "([time_stamp()]) ([user]) LOOC: - [text]" + /proc/log_whisper(text, mob/speaker) if (config.log_whisper) WRITE_LOG(diary, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)]") + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + + /proc/log_emote(text, mob/speaker) if (config.log_emote) WRITE_LOG(diary, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)]") + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" + /proc/log_attack(attacker, defender, message) if (config.log_attack) WRITE_LOG(diary, "ATTACK: [attacker] against [defender]: [message]") @@ -113,6 +133,10 @@ if (config.log_say) WRITE_LOG(diary, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)]") + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([src]/[speaker.client]) DEADSAY: - [text]" + + /proc/log_ghostemote(text, mob/speaker) if (config.log_emote) WRITE_LOG(diary, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)]") @@ -125,6 +149,10 @@ if (config.log_pda) WRITE_LOG(diary, "PDA: [speaker.simple_info_line()]: [html_decode(text)]") + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" + + /proc/log_to_dd(text) world.log << text //this comes before the config check because it can't possibly runtime if(config.log_world_output) @@ -222,7 +250,7 @@ if(include_link && is_special_character(M) && highlight_special_characters) name = "[name]" //Orange - + . += "/([name])" return . diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 96c97ec9f6..220f896436 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -235,6 +235,12 @@ Proc for attack log creation, because really why not var/atom/original_loc = user.loc + var/obj/mecha/M = null + + if(istype(user.loc, /obj/mecha)) + original_loc = get_turf(original_loc) + M = user.loc + var/holding = user.get_active_hand() var/datum/progressbar/progbar @@ -253,7 +259,12 @@ Proc for attack log creation, because really why not . = FALSE break - if(user.loc != original_loc && !ignore_movement) + if(M) + if(user.loc != M || (M.loc != original_loc && !ignore_movement)) // Mech coooooode. + . = FALSE + break + + else if(user.loc != original_loc && !ignore_movement) . = FALSE break diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index d4fa4a74bb..eb363c08cb 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -84,7 +84,6 @@ // 0 .. 9 if(48 to 57) //Numbers - if(!last_char_group) continue //suppress at start of string if(!allow_numbers) continue // If allow_numbers is 0, then don't do this. output += ascii2text(ascii_char) number_of_alphanumeric++ @@ -331,6 +330,55 @@ proc/TextPreview(var/string,var/len=40) /proc/strip_improper(var/text) return replacetext(replacetext(text, "\proper", ""), "\improper", "") +/proc/pencode2html(t) + t = replacetext(t, "\n", "
") + 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, "\[time\]", "[stationtime2text()]") + t = replacetext(t, "\[date\]", "[stationdate2text()]") + t = replacetext(t, "\[large\]", "") + t = replacetext(t, "\[/large\]", "") + t = replacetext(t, "\[field\]", "") + t = replacetext(t, "\[h1\]", "

") + t = replacetext(t, "\[/h1\]", "

") + t = replacetext(t, "\[h2\]", "

") + t = replacetext(t, "\[/h2\]", "

") + t = replacetext(t, "\[h3\]", "

") + t = replacetext(t, "\[/h3\]", "

") + t = replacetext(t, "\[*\]", "
  • ") + t = replacetext(t, "\[hr\]", "
    ") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + t = replacetext(t, "\[list\]", "") + t = replacetext(t, "\[table\]", "") + t = replacetext(t, "\[/table\]", "
    ") + t = replacetext(t, "\[grid\]", "") + t = replacetext(t, "\[/grid\]", "
    ") + t = replacetext(t, "\[row\]", "") + t = replacetext(t, "\[cell\]", "") + t = replacetext(t, "\[logo\]", "") + t = replacetext(t, "\[redlogo\]", "") + t = replacetext(t, "\[sglogo\]", "") + t = replacetext(t, "\[editorbr\]", "") + return t + +// Random password generator +/proc/GenerateKey() + //Feel free to move to Helpers. + var/newKey + newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") + newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") + newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") + return newKey + //Used for applying byonds text macros to strings that are loaded at runtime /proc/apply_text_macros(string) var/next_backslash = findtext(string, "\\") diff --git a/code/_macros.dm b/code/_macros.dm index 0af691a90c..3860f63434 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -11,6 +11,7 @@ #define log_world(message) world.log << message #define to_file(file_entry, source_var) file_entry << source_var #define from_file(file_entry, target_var) file_entry >> target_var +#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name) // From TG, might be useful to have. // Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose. @@ -20,4 +21,8 @@ #define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE) +#define qdel_null(x) if(x) { qdel(x) ; x = null } + +#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) + #define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index b9a07a60f2..19634bace7 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -114,6 +114,19 @@ trigger_aiming(TARGET_CAN_CLICK) return 1 + // VOREStation Addition Start: inbelly item interaction + if(isbelly(loc) && (loc == A.loc)) + if(W) + var/resolved = W.resolve_attackby(A,src) + if(!resolved && A && W) + W.afterattack(A, src, 1, params) // 1: clicking something Adjacent + else + if(ismob(A)) // No instant mob attacking + setClickCooldown(get_attack_speed()) + UnarmedAttack(A, 1) + return + // VOREStation Addition End + if(!isturf(loc)) // This is going to stop you from telekinesing from inside a closet, but I don't shed many tears for that return diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 2a92ba20bc..7060451b27 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -108,6 +108,22 @@ screen_loc = "WEST,SOUTH to EAST,NORTH" icon_state = "druggy" +/obj/screen/fullscreen/noise + icon = 'icons/effects/static.dmi' + icon_state = "1 light" + screen_loc = ui_entire_screen + layer = FULLSCREEN_LAYER + +/obj/screen/fullscreen/scanline + icon = 'icons/effects/static.dmi' + icon_state = "scanlines" + screen_loc = ui_entire_screen + alpha = 50 + layer = FULLSCREEN_LAYER + +/obj/screen/fullscreen/fishbed + icon_state = "fishbed" + #undef FULLSCREEN_LAYER #undef BLIND_LAYER #undef DAMAGE_LAYER diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm new file mode 100644 index 0000000000..e83e5d732c --- /dev/null +++ b/code/_onclick/hud/radial.dm @@ -0,0 +1,313 @@ +#define NEXT_PAGE_ID "__next__" +#define DEFAULT_CHECK_DELAY 20 + +GLOBAL_LIST_EMPTY(radial_menus) + +// Ported from TG + +/obj/screen/radial + icon = 'icons/mob/radial.dmi' + layer = LAYER_HUD_ABOVE + plane = PLANE_PLAYER_HUD_ABOVE + var/datum/radial_menu/parent + +/obj/screen/radial/slice + icon_state = "radial_slice" + var/choice + var/next_page = FALSE + var/tooltips = FALSE + +/obj/screen/radial/slice/MouseEntered(location, control, params) + . = ..() + icon_state = "radial_slice_focus" + if(tooltips) + openToolTip(usr, src, params, title = name) + +/obj/screen/radial/slice/MouseExited(location, control, params) + . = ..() + icon_state = "radial_slice" + if(tooltips) + closeToolTip(usr) + +/obj/screen/radial/slice/Click(location, control, params) + if(usr.client == parent.current_user) + if(next_page) + parent.next_page() + else + parent.element_chosen(choice,usr) + +/obj/screen/radial/center + name = "Close Menu" + icon_state = "radial_center" + +/obj/screen/radial/center/MouseEntered(location, control, params) + . = ..() + icon_state = "radial_center_focus" + +/obj/screen/radial/center/MouseExited(location, control, params) + . = ..() + icon_state = "radial_center" + +/obj/screen/radial/center/Click(location, control, params) + if(usr.client == parent.current_user) + parent.finished = TRUE + +/datum/radial_menu + var/list/choices = list() //List of choice id's + var/list/choices_icons = list() //choice_id -> icon + var/list/choices_values = list() //choice_id -> choice + var/list/page_data = list() //list of choices per page + + + var/selected_choice + var/list/obj/screen/elements = list() + var/obj/screen/radial/center/close_button + var/client/current_user + var/atom/anchor + var/image/menu_holder + var/finished = FALSE + var/datum/callback/custom_check_callback + var/next_check = 0 + var/check_delay = DEFAULT_CHECK_DELAY + + var/radius = 32 + var/starting_angle = 0 + var/ending_angle = 360 + var/zone = 360 + var/min_angle = 45 //Defaults are setup for this value, if you want to make the menu more dense these will need changes. + var/max_elements + var/pages = 1 + var/current_page = 1 + + var/hudfix_method = TRUE //TRUE to change anchor to user, FALSE to shift by py_shift + var/py_shift = 0 + var/entry_animation = TRUE + +//If we swap to vis_contens inventory these will need a redo +/datum/radial_menu/proc/check_screen_border(mob/user) + var/atom/movable/AM = anchor + if(!istype(AM) || !AM.screen_loc) + return + if(AM in user.client.screen) + if(hudfix_method) + anchor = user + else + py_shift = 32 + restrict_to_dir(NORTH) //I was going to parse screen loc here but that's more effort than it's worth. + +//Sets defaults +//These assume 45 deg min_angle +/datum/radial_menu/proc/restrict_to_dir(dir) + switch(dir) + if(NORTH) + starting_angle = 270 + ending_angle = 135 + if(SOUTH) + starting_angle = 90 + ending_angle = 315 + if(EAST) + starting_angle = 0 + ending_angle = 225 + if(WEST) + starting_angle = 180 + ending_angle = 45 + +/datum/radial_menu/proc/setup_menu(use_tooltips) + if(ending_angle > starting_angle) + zone = ending_angle - starting_angle + else + zone = 360 - starting_angle + ending_angle + + max_elements = round(zone / min_angle) + var/paged = max_elements < choices.len + if(elements.len < max_elements) + var/elements_to_add = max_elements - elements.len + for(var/i in 1 to elements_to_add) //Create all elements + var/obj/screen/radial/slice/new_element = new /obj/screen/radial/slice + new_element.tooltips = use_tooltips + new_element.parent = src + elements += new_element + + var/page = 1 + page_data = list(null) + var/list/current = list() + var/list/choices_left = choices.Copy() + while(choices_left.len) + if(current.len == max_elements) + page_data[page] = current + page++ + page_data.len++ + current = list() + if(paged && current.len == max_elements - 1) + current += NEXT_PAGE_ID + continue + else + current += popleft(choices_left) + if(paged && current.len < max_elements) + current += NEXT_PAGE_ID + + page_data[page] = current + pages = page + current_page = 1 + update_screen_objects(anim = entry_animation) + +/datum/radial_menu/proc/update_screen_objects(anim = FALSE) + var/list/page_choices = page_data[current_page] + var/angle_per_element = round(zone / page_choices.len) + for(var/i in 1 to elements.len) + var/obj/screen/radial/E = elements[i] + var/angle = WRAP(starting_angle + (i - 1) * angle_per_element,0,360) + if(i > page_choices.len) + HideElement(E) + else + SetElement(E,page_choices[i],angle,anim = anim,anim_order = i) + +/datum/radial_menu/proc/HideElement(obj/screen/radial/slice/E) + E.cut_overlays() + E.alpha = 0 + E.name = "None" + E.maptext = null + E.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + E.choice = null + E.next_page = FALSE + +/datum/radial_menu/proc/SetElement(obj/screen/radial/slice/E,choice_id,angle,anim,anim_order) + //Position + var/py = round(cos(angle) * radius) + py_shift + var/px = round(sin(angle) * radius) + if(anim) + var/timing = anim_order * 0.5 + var/matrix/starting = matrix() + starting.Scale(0.1,0.1) + E.transform = starting + var/matrix/TM = matrix() + animate(E,pixel_x = px,pixel_y = py, transform = TM, time = timing) + else + E.pixel_y = py + E.pixel_x = px + + //Visuals + E.alpha = 255 + E.mouse_opacity = MOUSE_OPACITY_ICON + E.cut_overlays() + if(choice_id == NEXT_PAGE_ID) + E.name = "Next Page" + E.next_page = TRUE + E.add_overlay("radial_next") + else + if(istext(choices_values[choice_id])) + E.name = choices_values[choice_id] + else + var/atom/movable/AM = choices_values[choice_id] //Movables only + E.name = AM.name + E.choice = choice_id + E.maptext = null + E.next_page = FALSE + if(choices_icons[choice_id]) + E.add_overlay(choices_icons[choice_id]) + +/datum/radial_menu/New() + close_button = new + close_button.parent = src + +/datum/radial_menu/proc/Reset() + choices.Cut() + choices_icons.Cut() + choices_values.Cut() + current_page = 1 + +/datum/radial_menu/proc/element_chosen(choice_id,mob/user) + selected_choice = choices_values[choice_id] + +/datum/radial_menu/proc/get_next_id() + return "c_[choices.len]" + +/datum/radial_menu/proc/set_choices(list/new_choices, use_tooltips) + if(choices.len) + Reset() + for(var/E in new_choices) + var/id = get_next_id() + choices += id + choices_values[id] = E + if(new_choices[E]) + var/I = extract_image(new_choices[E]) + if(I) + choices_icons[id] = I + setup_menu(use_tooltips) + + +/datum/radial_menu/proc/extract_image(E) + var/mutable_appearance/MA = new /mutable_appearance(E) + if(MA) + MA.layer = LAYER_HUD_ABOVE + MA.appearance_flags |= RESET_TRANSFORM + return MA + + +/datum/radial_menu/proc/next_page() + if(pages > 1) + current_page = WRAP(current_page + 1,1,pages+1) + update_screen_objects() + +/datum/radial_menu/proc/show_to(mob/M) + if(current_user) + hide() + if(!M.client || !anchor) + return + current_user = M.client + //Blank + menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing",layer = LAYER_HUD_ABOVE) + menu_holder.appearance_flags |= KEEP_APART + menu_holder.vis_contents += elements + close_button + current_user.images += menu_holder + +/datum/radial_menu/proc/hide() + if(current_user) + current_user.images -= menu_holder + +/datum/radial_menu/proc/wait(atom/user, atom/anchor, require_near = FALSE) + while (current_user && !finished && !selected_choice) + if(require_near && !in_range(anchor, user)) + return + if(custom_check_callback && next_check < world.time) + if(!custom_check_callback.Invoke()) + return + else + next_check = world.time + check_delay + stoplag(1) + +/datum/radial_menu/Destroy() + Reset() + hide() + QDEL_NULL(custom_check_callback) + . = ..() + +/* + Presents radial menu to user anchored to anchor (or user if the anchor is currently in users screen) + Choices should be a list where list keys are movables or text used for element names and return value + and list values are movables/icons/images used for element icons +*/ +/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE) + if(!user || !anchor || !length(choices)) + return + if(!uniqueid) + uniqueid = "defmenu_[REF(user)]_[REF(anchor)]" + + if(GLOB.radial_menus[uniqueid]) + return + + var/datum/radial_menu/menu = new + GLOB.radial_menus[uniqueid] = menu + if(radius) + menu.radius = radius + if(istype(custom_check)) + menu.custom_check_callback = custom_check + menu.anchor = anchor + menu.check_screen_border(user) //Do what's needed to make it look good near borders or on hud + menu.set_choices(choices, tooltips) + menu.show_to(user) + menu.wait(user, anchor, require_near) + var/answer = menu.selected_choice + QDEL_NULL(menu) + GLOB.radial_menus -= uniqueid + return answer \ No newline at end of file diff --git a/code/_onclick/hud/radial_persistent.dm b/code/_onclick/hud/radial_persistent.dm new file mode 100644 index 0000000000..feaf17c1b2 --- /dev/null +++ b/code/_onclick/hud/radial_persistent.dm @@ -0,0 +1,75 @@ +/* + A derivative of radial menu which persists onscreen until closed and invokes a callback each time an element is clicked +*/ + +/obj/screen/radial/persistent/center + name = "Close Menu" + icon_state = "radial_center" + +/obj/screen/radial/persistent/center/Click(location, control, params) + if(usr.client == parent.current_user) + parent.element_chosen(null,usr) + +/obj/screen/radial/persistent/center/MouseEntered(location, control, params) + . = ..() + icon_state = "radial_center_focus" + +/obj/screen/radial/persistent/center/MouseExited(location, control, params) + . = ..() + icon_state = "radial_center" + + + +/datum/radial_menu/persistent + var/uniqueid + var/datum/callback/select_proc_callback + +/datum/radial_menu/persistent/New() + close_button = new /obj/screen/radial/persistent/center + close_button.parent = src + + +/datum/radial_menu/persistent/element_chosen(choice_id,mob/user) + select_proc_callback.Invoke(choices_values[choice_id]) + + +/datum/radial_menu/persistent/proc/change_choices(list/newchoices, tooltips) + if(!newchoices.len) + return + Reset() + set_choices(newchoices,tooltips) + +/datum/radial_menu/persistent/Destroy() + QDEL_NULL(select_proc_callback) + GLOB.radial_menus -= uniqueid + Reset() + hide() + . = ..() + +/* + Creates a persistent radial menu and shows it to the user, anchored to anchor (or user if the anchor is currently in users screen). + Choices should be a list where list keys are movables or text used for element names and return value + and list values are movables/icons/images used for element icons + Select_proc is the proc to be called each time an element on the menu is clicked, and should accept the chosen element as its final argument + Clicking the center button will return a choice of null +*/ +/proc/show_radial_menu_persistent(mob/user, atom/anchor, list/choices, datum/callback/select_proc, uniqueid, radius, tooltips = FALSE) + if(!user || !anchor || !length(choices) || !select_proc) + return + if(!uniqueid) + uniqueid = "defmenu_[REF(user)]_[REF(anchor)]" + + if(GLOB.radial_menus[uniqueid]) + return + + var/datum/radial_menu/persistent/menu = new + menu.uniqueid = uniqueid + GLOB.radial_menus[uniqueid] = menu + if(radius) + menu.radius = radius + menu.select_proc_callback = select_proc + menu.anchor = anchor + menu.check_screen_border(user) //Do what's needed to make it look good near borders or on hud + menu.set_choices(choices, tooltips) + menu.show_to(user) + return menu diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index e367769b07..3bd1a0afb2 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -2,6 +2,9 @@ /atom/proc/attack_generic(mob/user as mob) return 0 +/atom/proc/take_damage(var/damage) + return 0 + /* Humans: Adds an exception for gloves, to allow special glove types like the ninja ones. diff --git a/code/controllers/Processes/planet.dm b/code/controllers/Processes/planet.dm deleted file mode 100644 index 18043f9270..0000000000 --- a/code/controllers/Processes/planet.dm +++ /dev/null @@ -1,96 +0,0 @@ -var/datum/controller/process/planet/planet_controller = null - -/datum/controller/process/planet - var/list/planets = list() - var/list/z_to_planet = list() - -/datum/controller/process/planet/setup() - name = "planet controller" - planet_controller = src - schedule_interval = 1 MINUTE - start_delay = 20 SECONDS - - var/list/planet_datums = typesof(/datum/planet) - /datum/planet - for(var/P in planet_datums) - var/datum/planet/NP = new P() - planets.Add(NP) - - allocateTurfs() - -/datum/controller/process/planet/proc/allocateTurfs() - for(var/turf/simulated/OT in outdoor_turfs) - for(var/datum/planet/P in planets) - if(OT.z in P.expected_z_levels) - P.planet_floors |= OT - OT.vis_contents |= P.weather_holder.visuals - break - outdoor_turfs.Cut() //Why were you in there INCORRECTLY? - - for(var/turf/unsimulated/wall/planetary/PW in planetary_walls) - for(var/datum/planet/P in planets) - if(PW.type == P.planetary_wall_type) - P.planet_walls |= PW - break - planetary_walls.Cut() - -/datum/controller/process/planet/proc/unallocateTurf(var/turf/T) - for(var/planet in planets) - var/datum/planet/P = planet - if(T.z in P.expected_z_levels) - P.planet_floors -= T - T.vis_contents -= P.weather_holder.visuals - -/datum/controller/process/planet/doWork() - if(outdoor_turfs.len || planetary_walls.len) - allocateTurfs() - - for(var/datum/planet/P in planets) - P.process(schedule_interval / 10) - SCHECK //Your process() really shouldn't take this long... - - //Sun light needs changing - if(P.needs_work & PLANET_PROCESS_SUN) - P.needs_work &= ~PLANET_PROCESS_SUN - // Remove old value from corners - var/list/sunlit_corners = P.sunlit_corners - var/old_lum_r = -P.sun["lum_r"] - var/old_lum_g = -P.sun["lum_g"] - var/old_lum_b = -P.sun["lum_b"] - if(old_lum_r || old_lum_g || old_lum_b) - for(var/C in P.sunlit_corners) - var/datum/lighting_corner/LC = C - LC.update_lumcount(old_lum_r, old_lum_g, old_lum_b) - SCHECK - sunlit_corners.Cut() - - // Calculate new values to apply - var/new_brightness = P.sun["brightness"] - var/new_color = P.sun["color"] - var/lum_r = new_brightness * GetRedPart (new_color) / 255 - var/lum_g = new_brightness * GetGreenPart(new_color) / 255 - var/lum_b = new_brightness * GetBluePart (new_color) / 255 - var/static/update_gen = -1 // Used to prevent double-processing corners. Otherwise would happen when looping over adjacent turfs. - for(var/I in P.planet_floors) - var/turf/simulated/T = I - if(!T.lighting_corners_initialised) - T.generate_missing_corners() - for(var/C in T.get_corners()) - var/datum/lighting_corner/LC = C - if(LC.update_gen != update_gen && LC.active) - sunlit_corners += LC - LC.update_gen = update_gen - LC.update_lumcount(lum_r, lum_g, lum_b) - SCHECK - update_gen-- - P.sun["lum_r"] = lum_r - P.sun["lum_g"] = lum_g - P.sun["lum_b"] = lum_b - - //Temperature needs updating - if(P.needs_work & PLANET_PROCESS_TEMP) - P.needs_work &= ~PLANET_PROCESS_TEMP - //Set new temperatures - for(var/W in P.planet_walls) - var/turf/unsimulated/wall/planetary/wall = W - wall.set_temperature(P.weather_holder.temperature) - SCHECK diff --git a/code/controllers/Processes/radiation.dm b/code/controllers/Processes/radiation.dm deleted file mode 100644 index 71d9c60233..0000000000 --- a/code/controllers/Processes/radiation.dm +++ /dev/null @@ -1,56 +0,0 @@ -/datum/controller/process/radiation - var/repository/radiation/linked = null - -/datum/controller/process/radiation/setup() - name = "radiation controller" - schedule_interval = 20 // every 2 seconds - linked = radiation_repository - -/datum/controller/process/radiation/doWork() - sources_decay() - cache_expires() - irradiate_targets() - -// Step 1 - Sources Decay -/datum/controller/process/radiation/proc/sources_decay() - var/list/sources = linked.sources - for(var/thing in sources) - var/datum/radiation_source/S = thing - if(QDELETED(S)) - sources.Remove(S) - continue - if(S.decay) - S.update_rad_power(S.rad_power - config.radiation_decay_rate) - if(S.rad_power <= config.radiation_lower_limit) - sources.Remove(S) - SCHECK // This scheck probably just wastes resources, but better safe than sorry in this case. - -// Step 2 - Cache Expires -/datum/controller/process/radiation/proc/cache_expires() - var/list/resistance_cache = linked.resistance_cache - for(var/thing in resistance_cache) - var/turf/T = thing - if(QDELETED(T)) - resistance_cache.Remove(T) - continue - if((length(T.contents) + 1) != resistance_cache[T]) - resistance_cache.Remove(T) // If its stale REMOVE it! It will get added if its needed. - SCHECK - -// Step 3 - Registered irradiatable things are checked for radiation -/datum/controller/process/radiation/proc/irradiate_targets() - var/list/registered_listeners = living_mob_list // For now just use this. Nothing else is interested anyway. - if(length(linked.sources) > 0) - for(var/thing in registered_listeners) - var/atom/A = thing - if(QDELETED(A)) - continue - var/turf/T = get_turf(thing) - var/rads = linked.get_rads_at_turf(T) - if(rads) - A.rad_act(rads) - SCHECK - -/datum/controller/process/radiation/statProcess() - ..() - stat(null, "[linked.sources.len] sources, [linked.resistance_cache.len] cached turfs") diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm deleted file mode 100644 index ac5e4696ab..0000000000 --- a/code/controllers/Processes/scheduler.dm +++ /dev/null @@ -1,169 +0,0 @@ -/var/datum/controller/process/scheduler/scheduler - -/************ -* Scheduler * -************/ -/datum/controller/process/scheduler - var/list/scheduled_tasks - -/datum/controller/process/scheduler/setup() - name = "scheduler" - schedule_interval = 1 SECOND - scheduled_tasks = list() - scheduler = src - -/datum/controller/process/scheduler/doWork() - var/world_time = world.time - for(last_object in scheduled_tasks) - var/datum/scheduled_task/scheduled_task = last_object - if(world_time < scheduled_task.trigger_time) - break // Too early for this one, and therefore too early for all remaining. - try - unschedule(scheduled_task) - scheduled_task.pre_process() - scheduled_task.process() - scheduled_task.post_process() - catch(var/exception/e) - catchException(e, last_object) - SCHECK - -// We've been restarted, probably due to having a massive list of tasks. -// Lets copy over the task list as safely as we can and try to chug thru it... -// Note: We won't be informed about tasks being destroyed, but this is the best we can do. -/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target) - scheduled_tasks = list() - for(var/datum/scheduled_task/st in target.scheduled_tasks) - if(!QDELETED(st) && istype(st)) - schedule(st) - scheduler = src - -// We are being killed. Least we can do is deregister all those events we registered -/datum/controller/process/scheduler/onKill() - for(var/st in scheduled_tasks) - GLOB.destroyed_event.unregister(st, src) - -/datum/controller/process/scheduler/statProcess() - ..() - stat(null, "[scheduled_tasks.len] task\s") - -/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st) - dd_insertObjectList(scheduled_tasks, st) - -/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st) - scheduled_tasks -= st - -/********** -* Helpers * -**********/ -/proc/schedule_task_in(var/in_time, var/procedure, var/list/arguments = list()) - return schedule_task(world.time + in_time, procedure, arguments) - -/proc/schedule_callback_in(var/in_time, var/datum/callback) - return schedule_callback(world.time + in_time, callback) - -/proc/schedule_task_with_source_in(var/in_time, var/source, var/procedure, var/list/arguments = list()) - return schedule_task_with_source(world.time + in_time, source, procedure, arguments) - -/proc/schedule_task(var/trigger_time, var/procedure, var/list/arguments) - var/datum/scheduled_task/st = new/datum/scheduled_task(trigger_time, procedure, arguments, /proc/destroy_scheduled_task, list()) - scheduler.schedule(st) - return st - -/proc/schedule_callback(var/trigger_time, var/datum/callback) - var/datum/scheduled_task/callback/st = new/datum/scheduled_task/callback(trigger_time, callback, /proc/destroy_scheduled_task, list()) - scheduler.schedule(st) - return st - -/proc/schedule_task_with_source(var/trigger_time, var/source, var/procedure, var/list/arguments) - var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/destroy_scheduled_task, list()) - scheduler.schedule(st) - return st - -/proc/schedule_repeating_task(var/trigger_time, var/repeat_interval, var/procedure, var/list/arguments) - var/datum/scheduled_task/st = new/datum/scheduled_task(trigger_time, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval)) - scheduler.schedule(st) - return st - -/proc/schedule_repeating_task_with_source(var/trigger_time, var/repeat_interval, var/source, var/procedure, var/list/arguments) - var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval)) - scheduler.schedule(st) - return st - -/************* -* Task Datum * -*************/ -/datum/scheduled_task - var/trigger_time - var/procedure - var/list/arguments - var/task_after_process - var/list/task_after_process_args - -/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args) - ..() - src.trigger_time = trigger_time - src.procedure = procedure - src.arguments = arguments ? arguments : list() - src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task - src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list() - task_after_process_args += src - -/datum/scheduled_task/Destroy() - scheduler.unschedule(src) - procedure = null - arguments.Cut() - task_after_process = null - task_after_process_args.Cut() - return ..() - -/datum/scheduled_task/dd_SortValue() - return trigger_time - -/datum/scheduled_task/proc/pre_process() - task_triggered_event.raise_event(list(src)) - -/datum/scheduled_task/proc/process() - if(procedure) - call(procedure)(arglist(arguments)) - -/datum/scheduled_task/proc/post_process() - call(task_after_process)(arglist(task_after_process_args)) - -// Resets the trigger time, has no effect if the task has already triggered -/datum/scheduled_task/proc/trigger_task_in(var/trigger_in) - src.trigger_time = world.time + trigger_in - -/datum/scheduled_task/callback - var/datum/callback/callback - -/datum/scheduled_task/callback/New(var/trigger_time, var/datum/callback, var/proc/task_after_process, var/list/task_after_process_args) - src.callback = callback - ..(trigger_time = trigger_time, task_after_process = task_after_process, task_after_process_args = task_after_process_args) - -/datum/scheduled_task/callback/process() - callback.Invoke() - -/datum/scheduled_task/source - var/datum/source - -/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args) - src.source = source - GLOB.destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed) - ..(trigger_time, procedure, arguments, task_after_process, task_after_process_args) - -/datum/scheduled_task/source/Destroy() - source = null - return ..() - -/datum/scheduled_task/source/process() - call(source, procedure)(arglist(arguments)) - -/datum/scheduled_task/source/proc/source_destroyed() - qdel(src) - -/proc/destroy_scheduled_task(var/datum/scheduled_task/st) - qdel(st) - -/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st) - st.trigger_time = world.time + trigger_delay - scheduler.schedule(st) diff --git a/code/controllers/Processes/supply.dm b/code/controllers/Processes/supply.dm index f71cd8fd1d..b2b9951cb6 100644 --- a/code/controllers/Processes/supply.dm +++ b/code/controllers/Processes/supply.dm @@ -224,8 +224,12 @@ var/datum/controller/supply/supply_controller = new() if(SP.access) if(isnum(SP.access)) A.req_access = list(SP.access) - else if(islist(SP.access)) + else if(islist(SP.access) && SP.one_access) var/list/L = SP.access // access var is a plain var, we need a list + A.req_one_access = L.Copy() + A.req_access.Cut() + else if(islist(SP.access) && !SP.one_access) + var/list/L = SP.access A.req_access = L.Copy() else log_debug("Supply pack with invalid access restriction [SP.access] encountered!") diff --git a/code/controllers/configuration_vr.dm b/code/controllers/configuration_vr.dm index 804fbc4ec8..cde9c7c963 100644 --- a/code/controllers/configuration_vr.dm +++ b/code/controllers/configuration_vr.dm @@ -5,9 +5,11 @@ /datum/configuration var/list/engine_map // Comma separated list of engines to choose from. Blank means fully random. var/time_off = FALSE + var/pto_job_change = FALSE var/limit_interns = -1 //Unlimited by default var/limit_visitors = -1 //Unlimited by default var/pto_cap = 100 //Hours + var/require_flavor = FALSE /hook/startup/proc/read_vs_config() var/list/Lines = file2list("config/config.txt") @@ -52,5 +54,8 @@ config.pto_cap = text2num(value) if ("time_off") config.time_off = TRUE - + if ("pto_job_change") + config.pto_job_change = TRUE + if ("require_flavor") + config.require_flavor = TRUE return 1 diff --git a/code/controllers/emergency_shuttle_controller.dm b/code/controllers/emergency_shuttle_controller.dm index 5dbfeb427a..2229c4febc 100644 --- a/code/controllers/emergency_shuttle_controller.dm +++ b/code/controllers/emergency_shuttle_controller.dm @@ -35,7 +35,12 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle if (!shuttle.location) //leaving from the station //launch the pods! - for (var/datum/shuttle/ferry/escape_pod/pod in escape_pods) + for (var/EP in escape_pods) + var/datum/shuttle/ferry/escape_pod/pod + if(istype(escape_pods[EP], /datum/shuttle/ferry/escape_pod)) + pod = escape_pods[EP] + else + continue if (!pod.arming_controller || pod.arming_controller.armed) pod.launch(src) @@ -57,7 +62,12 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle //arm the escape pods if (evac) - for (var/datum/shuttle/ferry/escape_pod/pod in escape_pods) + for (var/EP in escape_pods) + var/datum/shuttle/ferry/escape_pod/pod + if(istype(escape_pods[EP], /datum/shuttle/ferry/escape_pod)) + pod = escape_pods[EP] + else + continue if (pod.arming_controller) pod.arming_controller.arm() diff --git a/code/controllers/subsystems/character_setup.dm b/code/controllers/subsystems/character_setup.dm new file mode 100644 index 0000000000..36851b26d4 --- /dev/null +++ b/code/controllers/subsystems/character_setup.dm @@ -0,0 +1,38 @@ +SUBSYSTEM_DEF(character_setup) + name = "Character Setup" + init_order = INIT_ORDER_DEFAULT + priority = FIRE_PRIORITY_CHARSETUP + flags = SS_BACKGROUND + wait = 1 SECOND + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + + var/list/prefs_awaiting_setup = list() + var/list/preferences_datums = list() + var/list/newplayers_requiring_init = list() + + var/list/save_queue = list() +/* +/datum/controller/subsystem/character_setup/Initialize() + while(prefs_awaiting_setup.len) + var/datum/preferences/prefs = prefs_awaiting_setup[prefs_awaiting_setup.len] + prefs_awaiting_setup.len-- + prefs.setup() + while(newplayers_requiring_init.len) + var/mob/new_player/new_player = newplayers_requiring_init[newplayers_requiring_init.len] + newplayers_requiring_init.len-- + new_player.deferred_login() + . = ..() +*/ //Might be useful if we ever switch to Bay prefs. +/datum/controller/subsystem/character_setup/fire(resumed = FALSE) + while(save_queue.len) + var/datum/preferences/prefs = save_queue[save_queue.len] + save_queue.len-- + + if(!QDELETED(prefs)) + prefs.save_preferences() + + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/character_setup/proc/queue_preferences_save(var/datum/preferences/prefs) + save_queue |= prefs diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index a741869b5b..9938fa20eb 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -240,9 +240,9 @@ SUBSYSTEM_DEF(garbage) time = TICK_DELTA_TO_MS(tick)/100 if (time > highest_del_time) highest_del_time = time - if (time > 10) - log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)") - message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).") + if (time > 20) //VOREStation Edit + log_game("Error: [type]([refID]) took longer than 2 seconds to delete (took [time/10] seconds to delete)") //VOREStation Edit + message_admins("Error: [type]([refID]) took longer than 2 seconds to delete (took [time/10] seconds to delete).") //VOREStation Edit postpone(time) /datum/controller/subsystem/garbage/proc/HardQueue(datum/D) diff --git a/code/controllers/subsystems/nanoui.dm b/code/controllers/subsystems/nanoui.dm index b4ceee4d63..520973514d 100644 --- a/code/controllers/subsystems/nanoui.dm +++ b/code/controllers/subsystems/nanoui.dm @@ -12,6 +12,8 @@ SUBSYSTEM_DEF(nanoui) var/list/nano_asset_dirs = list(\ "nano/css/",\ "nano/images/",\ + "nano/images/status_icons/",\ + "nano/images/modular_computers/",\ "nano/js/",\ "nano/templates/"\ ) @@ -23,9 +25,9 @@ SUBSYSTEM_DEF(nanoui) if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore if(fexists(path + filename)) asset_files.Add(fcopy_rsc(path + filename)) // add this file to asset_files for sending to clients when they connect - .=..() //VOREStation Edit start: fixing some kevinzing + .=..() for(var/i in GLOB.clients) - send_resources(i) //VOREStation Edit end: fixing some kevinzing + send_resources(i) /datum/controller/subsystem/nanoui/Recover() if(SSnanoui.open_uis) @@ -48,4 +50,4 @@ SUBSYSTEM_DEF(nanoui) if(!subsystem_initialized) return for(var/file in asset_files) - client << browse_rsc(file) // send the file to the client \ No newline at end of file + client << browse_rsc(file) // send the file to the client diff --git a/code/controllers/subsystems/radiation.dm b/code/controllers/subsystems/radiation.dm new file mode 100644 index 0000000000..babc2c7d5d --- /dev/null +++ b/code/controllers/subsystems/radiation.dm @@ -0,0 +1,135 @@ +SUBSYSTEM_DEF(radiation) + name = "Radiation" + wait = 2 SECONDS + flags = SS_NO_INIT + + var/list/sources = list() // all radiation source datums + var/list/sources_assoc = list() // Sources indexed by turf for de-duplication. + var/list/resistance_cache = list() // Cache of turf's radiation resistance. + + var/tmp/list/current_sources = list() + var/tmp/list/current_res_cache = list() + var/tmp/list/listeners = list() + +/datum/controller/subsystem/radiation/fire(resumed = FALSE) + if (!resumed) + current_sources = sources.Copy() + current_res_cache = resistance_cache.Copy() + listeners = living_mob_list.Copy() + + while(current_sources.len) + var/datum/radiation_source/S = current_sources[current_sources.len] + current_sources.len-- + + if(QDELETED(S)) + sources -= S + else if(S.decay) + S.update_rad_power(S.rad_power - config.radiation_decay_rate) + if (MC_TICK_CHECK) + return + + while(current_res_cache.len) + var/turf/T = current_res_cache[current_res_cache.len] + current_res_cache.len-- + + if(QDELETED(T)) + resistance_cache -= T + else if((length(T.contents) + 1) != resistance_cache[T]) + resistance_cache -= T // If its stale REMOVE it! It will get added if its needed. + if (MC_TICK_CHECK) + return + + if(!sources.len) + listeners.Cut() + + while(listeners.len) + var/atom/A = listeners[listeners.len] + listeners.len-- + + if(!QDELETED(A)) + var/turf/T = get_turf(A) + var/rads = get_rads_at_turf(T) + if(rads) + A.rad_act(rads) + if (MC_TICK_CHECK) + return + +/datum/controller/subsystem/radiation/stat_entry() + ..("S:[sources.len], RC:[resistance_cache.len]") + +// Ray trace from all active radiation sources to T and return the strongest effect. +/datum/controller/subsystem/radiation/proc/get_rads_at_turf(var/turf/T) + . = 0 + if(!istype(T)) + return + + for(var/value in sources) + var/datum/radiation_source/source = value + if(source.rad_power < .) + continue // Already being affected by a stronger source + if(source.source_turf.z != T.z) + continue // Radiation is not multi-z + if(source.respect_maint) + var/area/A = T.loc + if(A.flags & RAD_SHIELDED) + continue // In shielded area + + var/dist = get_dist(source.source_turf, T) + if(dist > source.range) + continue // Too far to possibly affect + if(source.flat) + . = max(., source.rad_power) + continue // No need to ray trace for flat field + + // Okay, now ray trace to find resistence! + var/turf/origin = source.source_turf + var/working = source.rad_power + while(origin != T) + origin = get_step_towards(origin, T) //Raytracing + if(!resistance_cache[origin]) //Only get the resistance if we don't already know it. + origin.calc_rad_resistance() + if(origin.cached_rad_resistance) + working = round((working / (origin.cached_rad_resistance * config.radiation_resistance_multiplier)), 0.1) + if((working <= .) || (working <= RADIATION_THRESHOLD_CUTOFF)) + break // Already affected by a stronger source (or its zero...) + . = max((working / (dist ** 2)), .) //Butchered version of the inverse square law. Works for this purpose + if(. <= RADIATION_THRESHOLD_CUTOFF) + . = 0 + +// Add a radiation source instance to the repository. It will override any existing source on the same turf. +/datum/controller/subsystem/radiation/proc/add_source(var/datum/radiation_source/S) + if(!isturf(S.source_turf)) + return + var/datum/radiation_source/existing = sources_assoc[S.source_turf] + if(existing) + qdel(existing) + sources += S + sources_assoc[S.source_turf] = S + +// Creates a temporary radiation source that will decay +/datum/controller/subsystem/radiation/proc/radiate(source, power) //Sends out a radiation pulse, taking walls into account + if(!(source && power)) //Sanity checking + return + var/datum/radiation_source/S = new() + S.source_turf = get_turf(source) + S.update_rad_power(power) + add_source(S) + +// Sets the radiation in a range to a constant value. +/datum/controller/subsystem/radiation/proc/flat_radiate(source, power, range, var/respect_maint = TRUE) //VOREStation edit; Respect shielded areas by default please. + if(!(source && power && range)) + return + var/datum/radiation_source/S = new() + S.flat = TRUE + S.range = range + S.respect_maint = respect_maint + S.source_turf = get_turf(source) + S.update_rad_power(power) + add_source(S) + +// Irradiates a full Z-level. Hacky way of doing it, but not too expensive. +/datum/controller/subsystem/radiation/proc/z_radiate(var/atom/source, power, var/respect_maint = TRUE) //VOREStation edit; Respect shielded areas by default please. + if(!(power && source)) + return + var/turf/epicentre = locate(round(world.maxx / 2), round(world.maxy / 2), source.z) + flat_radiate(epicentre, power, world.maxx, respect_maint) \ No newline at end of file diff --git a/code/controllers/subsystems/timer.dm b/code/controllers/subsystems/timer.dm index b743e8ff9e..3dcc64db28 100644 --- a/code/controllers/subsystems/timer.dm +++ b/code/controllers/subsystems/timer.dm @@ -1,12 +1,11 @@ -#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth) -#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN)||BUCKET_LEN) +#define BUCKET_LEN (round(10*(60/world.tick_lag), 1)) //how many ticks should we keep in the bucket. (1 minutes worth) +#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN) #define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1))) #define TIMER_ID_MAX (2**24) //max float with integer precision SUBSYSTEM_DEF(timer) name = "Timer" wait = 1 //SS_TICKER subsystem, so wait is in ticks - priority = FIRE_PRIORITY_TIMERS //VOREStation Emergency Edit init_order = INIT_ORDER_TIMER flags = SS_TICKER|SS_NO_INIT @@ -95,8 +94,8 @@ SUBSYSTEM_DEF(timer) if(ctime_timer.flags & TIMER_LOOP) ctime_timer.spent = 0 - clienttime_timers.Insert(ctime_timer, 1) - cut_start_index++ + ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait + BINARY_INSERT(ctime_timer, clienttime_timers, datum/timedevent, timeToRun) else qdel(ctime_timer) @@ -270,7 +269,7 @@ SUBSYSTEM_DEF(timer) var/new_bucket_count var/i = 1 for (i in 1 to length(alltimers)) - var/datum/timedevent/timer = alltimers[1] + var/datum/timedevent/timer = alltimers[i] if (!timer) continue diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm index e7c3d5aaf9..54fb487985 100644 --- a/code/controllers/subsystems/vote.dm +++ b/code/controllers/subsystems/vote.dm @@ -105,7 +105,7 @@ SUBSYSTEM_DEF(vote) factor = 1.4 choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) world << "Crew Transfer Factor: [factor]" - greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) + greatest_votes = max(choices["Initiate Crew Transfer"], choices["Extend the Shift"]) //VOREStation Edit . = list() // Get all options with that many votes and return them in a list if(greatest_votes) @@ -220,8 +220,8 @@ SUBSYSTEM_DEF(vote) if(ticker.current_state <= GAME_STATE_SETTING_UP) initiator_key << "The crew transfer button has been disabled!" return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") + question = "Your PDA beeps with a message from Central. Would you like an additional hour to finish ongoing projects?" //VOREStation Edit + choices.Add("Initiate Crew Transfer", "Extend the Shift") //VOREStation Edit if(VOTE_ADD_ANTAGONIST) if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP) return 0 diff --git a/code/datums/autolathe/arms_vr.dm b/code/datums/autolathe/arms_vr.dm index 72f0b9d15a..546141457d 100644 --- a/code/datums/autolathe/arms_vr.dm +++ b/code/datums/autolathe/arms_vr.dm @@ -32,3 +32,21 @@ name = "magazine (.44 rubber)" path =/obj/item/ammo_magazine/m44/rubber hidden = 1 + +/datum/category_item/autolathe/arms/classic_smg_9mm + name = "SMG magazine (9mm)" + path = /obj/item/ammo_magazine/m9mml + hidden = 1 +/* De-coded? +/datum/category_item/autolathe/arms/classic_smg_9mmr + name = "SMG magazine (9mm rubber)" + path = /obj/item/ammo_magazine/m9mml/rubber + +/datum/category_item/autolathe/arms/classic_smg_9mmp + name = "SMG magazine (9mm practice)" + path = /obj/item/ammo_magazine/m9mml/practice + +/datum/category_item/autolathe/arms/classic_smg_9mmf + name = "SMG magazine (9mm flash)" + path = /obj/item/ammo_magazine/m9mml/flash +*/ \ No newline at end of file diff --git a/code/datums/autolathe/autolathe.dm b/code/datums/autolathe/autolathe.dm index 4adeb85760..91c9ec37b7 100644 --- a/code/datums/autolathe/autolathe.dm +++ b/code/datums/autolathe/autolathe.dm @@ -6,10 +6,14 @@ var/datum/category_collection/autolathe/autolathe_recipes if(I.matter && !resources) resources = list() for(var/material in I.matter) - resources[material] = I.matter[material]*1.25 // More expensive to produce than they are to recycle. - if(is_stack && istype(I, /obj/item/stack)) - var/obj/item/stack/IS = I - max_stack = IS.max_amount + var/coeff = (no_scale ? 1 : 1.25) //most objects are more expensive to produce than to recycle + resources[material] = I.matter[material]*coeff // but if it's a sheet or RCD cartridge, it's 1:1 + if(is_stack) + if(istype(I, /obj/item/stack)) + var/obj/item/stack/IS = I + max_stack = IS.max_amount + else + max_stack = 10 qdel(I) /**************************** @@ -64,8 +68,9 @@ var/datum/category_collection/autolathe/autolathe_recipes var/list/resources var/hidden var/power_use = 0 - var/is_stack + var/is_stack // Creates multiple of an item if applied to non-stack items var/max_stack + var/no_scale /datum/category_item/autolathe/dd_SortValue() return name \ No newline at end of file diff --git a/code/datums/autolathe/engineering.dm b/code/datums/autolathe/engineering.dm index ca40b4a6a9..fd907ba5f8 100644 --- a/code/datums/autolathe/engineering.dm +++ b/code/datums/autolathe/engineering.dm @@ -93,6 +93,7 @@ /datum/category_item/autolathe/engineering/rcd_ammo name = "matter cartridge" path =/obj/item/weapon/rcd_ammo + no_scale = TRUE //prevents material duplication exploits /datum/category_item/autolathe/engineering/rcd name = "rapid construction device" diff --git a/code/datums/autolathe/engineering_vr.dm b/code/datums/autolathe/engineering_vr.dm new file mode 100644 index 0000000000..08e1cf986b --- /dev/null +++ b/code/datums/autolathe/engineering_vr.dm @@ -0,0 +1,7 @@ +/datum/category_item/autolathe/engineering/timeclock + name = "timeclock electronics" + path =/obj/item/weapon/circuitboard/timeclock + +/datum/category_item/autolathe/engineering/id_restorer + name = "ID restoration console electronics" + path =/obj/item/weapon/circuitboard/id_restorer \ No newline at end of file diff --git a/code/datums/autolathe/general.dm b/code/datums/autolathe/general.dm index f354583318..2f4436bf55 100644 --- a/code/datums/autolathe/general.dm +++ b/code/datums/autolathe/general.dm @@ -73,22 +73,26 @@ /datum/category_item/autolathe/general/metal name = "steel sheets" path =/obj/item/stack/material/steel - is_stack = 1 + is_stack = TRUE + no_scale = TRUE //prevents material duplication exploits /datum/category_item/autolathe/general/glass name = "glass sheets" path =/obj/item/stack/material/glass - is_stack = 1 + is_stack = TRUE + no_scale = TRUE //prevents material duplication exploits /datum/category_item/autolathe/general/rglass name = "reinforced glass sheets" path =/obj/item/stack/material/glass/reinforced - is_stack = 1 + is_stack = TRUE + no_scale = TRUE //prevents material duplication exploits /datum/category_item/autolathe/general/rods name = "metal rods" path =/obj/item/stack/rods - is_stack = 1 + is_stack = TRUE + no_scale = TRUE //prevents material duplication exploits /datum/category_item/autolathe/general/knife name = "kitchen knife" @@ -101,10 +105,12 @@ /datum/category_item/autolathe/general/tube name = "light tube" path =/obj/item/weapon/light/tube + is_stack = TRUE /datum/category_item/autolathe/general/bulb name = "light bulb" path =/obj/item/weapon/light/bulb + is_stack = TRUE /datum/category_item/autolathe/general/ashtray_glass name = "glass ashtray" diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 1ece4c93a6..518db6f9ec 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -19,6 +19,7 @@ var/list/med = new() var/list/sci = new() var/list/car = new() + var/list/pla = new() //VOREStation Edit var/list/civ = new() var/list/bot = new() var/list/misc = new() @@ -72,6 +73,11 @@ if(real_rank in cargo_positions) car[name] = rank department = 1 + //VOREStation Edit Begin + if(real_rank in planet_positions) + pla[name] = rank + department = 1 + //VOREStation Edit End if(real_rank in civilian_positions) civ[name] = rank department = 1 @@ -118,6 +124,13 @@ for(name in car) dat += "[name][car[name]][isactive[name]]" even = !even + //VOREStation Edit Begin + if(pla.len > 0) + dat += "Exploration" + for(name in pla) + dat += "[name][pla[name]][isactive[name]]" + even = !even + //VOREStation Edit End if(civ.len > 0) dat += "Civilian" for(name in civ) diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index 2f7942a9b2..427b38f73b 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -117,7 +117,7 @@ /datum/ghost_query/lost_drone role_name = "Lost Drone" question = "A lost drone onboard has been discovered by a crewmember and they are attempting to reactivate it. Would you like to play as the drone?" - be_special_flag = BE_AI + //be_special_flag = BE_AI //VOREStation Removal: Positronic role is never used because intended purpose is unfitting, so remove the check check_bans = list("AI", "Cyborg") cutoff_number = 1 diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 541ba77194..089a6be722 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -205,7 +205,7 @@ if(telemob.can_be_drop_prey && telenommer.can_be_drop_pred) return 1 obstructed = 1 - else if(!isturf(destination.loc) || !destination.x || !destination.y || !destination.z) //If we're inside something or outside universe + else if(!((isturf(destination) && !destination.density) || (isturf(destination.loc) && !destination.loc.density)) || !destination.x || !destination.y || !destination.z) //If we're inside something or outside universe obstructed = 1 to_chat(teleatom, "Something is blocking way on the other side!") if(obstructed) diff --git a/code/datums/helper_datums/teleport_vr.dm b/code/datums/helper_datums/teleport_vr.dm index 4075bc70f0..d8e8428535 100644 --- a/code/datums/helper_datums/teleport_vr.dm +++ b/code/datums/helper_datums/teleport_vr.dm @@ -1,3 +1,11 @@ +//wrapper +/proc/do_noeffect_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=FALSE) + new /datum/teleport/instant/science/noeffect(arglist(args)) + return + +/datum/teleport/instant/science/noeffect/setEffects(datum/effect/effect/system/aeffectin,datum/effect/effect/system/aeffectout) + return 1 + /datum/teleport/proc/try_televore() //Destination is in a belly if(isbelly(destination.loc)) diff --git a/code/datums/outfits/jobs/civilian.dm b/code/datums/outfits/jobs/civilian.dm index f3237249b1..c6d6194f17 100644 --- a/code/datums/outfits/jobs/civilian.dm +++ b/code/datums/outfits/jobs/civilian.dm @@ -6,11 +6,6 @@ name = OUTFIT_JOB_NAME("Visitor") id_pda_assignment = "Visitor" uniform = /obj/item/clothing/under/assistantformal - -/decl/hierarchy/outfit/job/assistant/resident - name = OUTFIT_JOB_NAME("Resident") - id_pda_assignment = "Resident" - uniform = /obj/item/clothing/under/color/white //VOREStation Add - Interns /decl/hierarchy/outfit/job/assistant/intern name = OUTFIT_JOB_NAME("Intern") @@ -49,6 +44,14 @@ name = OUTFIT_JOB_NAME("Cook") id_pda_assignment = "Cook" +// Rykka adds Server Outfit + +/decl/hierarchy/outfit/job/service/server + name = OUTFIT_JOB_NAME("Server") + uniform = /obj/item/clothing/under/waiter + +// End Outfit addition + /decl/hierarchy/outfit/job/service/gardener name = OUTFIT_JOB_NAME("Gardener") uniform = /obj/item/clothing/under/rank/hydroponics diff --git a/code/datums/outfits/jobs/civilian_vr.dm b/code/datums/outfits/jobs/civilian_vr.dm new file mode 100644 index 0000000000..7d39ae8c1b --- /dev/null +++ b/code/datums/outfits/jobs/civilian_vr.dm @@ -0,0 +1,18 @@ +/decl/hierarchy/outfit/job/assistant/worker + id_type = /obj/item/weapon/card/id/civilian + +/decl/hierarchy/outfit/job/assistant/cargo + id_type = /obj/item/weapon/card/id/cargo + +/decl/hierarchy/outfit/job/assistant/engineer + id_type = /obj/item/weapon/card/id/engineering + flags = OUTFIT_HAS_BACKPACK|OUTFIT_EXTENDED_SURVIVAL + +/decl/hierarchy/outfit/job/assistant/medic + id_type = /obj/item/weapon/card/id/medical + +/decl/hierarchy/outfit/job/assistant/scientist + id_type = /obj/item/weapon/card/id/science + +/decl/hierarchy/outfit/job/assistant/officer + id_type = /obj/item/weapon/card/id/security diff --git a/code/datums/outfits/jobs/command.dm b/code/datums/outfits/jobs/command.dm index 8709bb9a79..eec17452ad 100644 --- a/code/datums/outfits/jobs/command.dm +++ b/code/datums/outfits/jobs/command.dm @@ -36,7 +36,7 @@ l_ear = /obj/item/device/radio/headset/headset_com shoes = /obj/item/clothing/shoes/brown id_type = /obj/item/weapon/card/id/silver/secretary - pda_type = /obj/item/device/pda/heads/hop + pda_type = /obj/item/device/pda/heads r_hand = /obj/item/weapon/clipboard /decl/hierarchy/outfit/job/secretary/pre_equip(mob/living/carbon/human/H) diff --git a/code/datums/outfits/military/fleet.dm b/code/datums/outfits/military/fleet.dm index cf5028978d..915917918c 100644 --- a/code/datums/outfits/military/fleet.dm +++ b/code/datums/outfits/military/fleet.dm @@ -1,21 +1,21 @@ /decl/hierarchy/outfit/military/fleet/pt name = OUTFIT_MILITARY("Fleet PT") - uniform = /obj/item/clothing/under/pt/fleet + uniform = /obj/item/clothing/under/solgov/pt/fleet shoes = /obj/item/clothing/shoes/black /decl/hierarchy/outfit/military/fleet/utility name = OUTFIT_MILITARY("Fleet Utility") - uniform = /obj/item/clothing/under/utility/fleet - shoes = /obj/item/clothing/shoes/boots/jackboots + uniform = /obj/item/clothing/under/solgov/utility/fleet + shoes = /obj/item/clothing/shoes/boots/duty /decl/hierarchy/outfit/military/fleet/service name = OUTFIT_MILITARY("Fleet Service") - uniform = /obj/item/clothing/under/service/fleet + uniform = /obj/item/clothing/under/solgov/service/fleet shoes = /obj/item/clothing/shoes/dress/white /decl/hierarchy/outfit/military/fleet/dress name = OUTFIT_MILITARY("Fleet Dress") - uniform = /obj/item/clothing/under/service/fleet + uniform = /obj/item/clothing/under/solgov/service/fleet shoes = /obj/item/clothing/shoes/dress/white suit = /obj/item/clothing/suit/storage/toggle/dress/fleet gloves = /obj/item/clothing/gloves/white diff --git a/code/datums/outfits/military/marines.dm b/code/datums/outfits/military/marines.dm index d818dabcaa..719b36c690 100644 --- a/code/datums/outfits/military/marines.dm +++ b/code/datums/outfits/military/marines.dm @@ -1,22 +1,22 @@ /decl/hierarchy/outfit/military/marine/pt name = OUTFIT_MILITARY("Marine PT") - uniform = /obj/item/clothing/under/pt/marine + uniform = /obj/item/clothing/under/solgov/pt/marine shoes = /obj/item/clothing/shoes/black /decl/hierarchy/outfit/military/marine/utility name = OUTFIT_MILITARY("Marine Utility") - uniform = /obj/item/clothing/under/utility/marine + uniform = /obj/item/clothing/under/solgov/utility/marine shoes = /obj/item/clothing/shoes/boots/jungle /decl/hierarchy/outfit/military/marine/service name = OUTFIT_MILITARY("Marine Service") - uniform = /obj/item/clothing/under/service/marine + uniform = /obj/item/clothing/under/solgov/service/marine shoes = /obj/item/clothing/shoes/dress suit = /obj/item/clothing/suit/storage/service/marine /decl/hierarchy/outfit/military/marine/dress name = OUTFIT_MILITARY("Marine Dress") - uniform = /obj/item/clothing/under/mildress/marine + uniform = /obj/item/clothing/under/solgov/mildress/marine shoes = /obj/item/clothing/shoes/dress/white suit = /obj/item/clothing/suit/dress/marine gloves = /obj/item/clothing/gloves/white diff --git a/code/datums/outfits/military/sifguard.dm b/code/datums/outfits/military/sifguard.dm index e1280f0cfe..9348224a9e 100644 --- a/code/datums/outfits/military/sifguard.dm +++ b/code/datums/outfits/military/sifguard.dm @@ -1,22 +1,22 @@ /decl/hierarchy/outfit/military/sifguard/pt name = OUTFIT_MILITARY("SifGuard PT") - uniform = /obj/item/clothing/under/pt/sifguard + uniform = /obj/item/clothing/under/solgov/pt/sifguard shoes = /obj/item/clothing/shoes/black /decl/hierarchy/outfit/military/sifguard/utility name = OUTFIT_MILITARY("SifGuard Utility") - uniform = /obj/item/clothing/under/utility/sifguard - shoes = /obj/item/clothing/shoes/boots/jackboots + uniform = /obj/item/clothing/under/solgov/utility/sifguard + shoes = /obj/item/clothing/shoes/boots/tactical /decl/hierarchy/outfit/military/sifguard/service name = OUTFIT_MILITARY("SifGuard Service") - uniform = /obj/item/clothing/under/utility/sifguard - shoes = /obj/item/clothing/shoes/boots/jackboots + uniform = /obj/item/clothing/under/solgov/utility/sifguard + shoes = /obj/item/clothing/shoes/boots/tactical suit = /obj/item/clothing/suit/storage/service/sifguard /decl/hierarchy/outfit/military/sifguard/dress name = OUTFIT_MILITARY("SifGuard Dress") - uniform = /obj/item/clothing/under/mildress/sifguard + uniform = /obj/item/clothing/under/solgov/mildress/sifguard shoes = /obj/item/clothing/shoes/dress suit = /obj/item/clothing/suit/dress/expedition gloves = /obj/item/clothing/gloves/white diff --git a/code/datums/outfits/outfit_vr.dm b/code/datums/outfits/outfit_vr.dm index 6177836a75..27e5cde50e 100644 --- a/code/datums/outfits/outfit_vr.dm +++ b/code/datums/outfits/outfit_vr.dm @@ -1,6 +1,6 @@ /decl/hierarchy/outfit/USDF/Marine name = "USDF marine" - uniform = /obj/item/clothing/under/utility/marine/green + uniform = /obj/item/clothing/under/solgov/utility/marine/green shoes = /obj/item/clothing/shoes/boots/jackboots gloves = /obj/item/clothing/gloves/combat l_ear = /obj/item/device/radio/headset/centcom @@ -30,7 +30,7 @@ head = /obj/item/clothing/head/dress/marine/command/admiral shoes = /obj/item/clothing/shoes/boots/jackboots l_ear = /obj/item/device/radio/headset/centcom - uniform = /obj/item/clothing/under/mildress/marine/command + uniform = /obj/item/clothing/under/solgov/mildress/marine/command back = /obj/item/weapon/storage/backpack/satchel belt = /obj/item/weapon/gun/projectile/revolver/consul l_pocket = /obj/item/ammo_magazine/s44 diff --git a/code/datums/repositories/radiation.dm b/code/datums/repositories/radiation.dm deleted file mode 100644 index 4525032e20..0000000000 --- a/code/datums/repositories/radiation.dm +++ /dev/null @@ -1,138 +0,0 @@ -var/global/repository/radiation/radiation_repository = new() - -/repository/radiation - var/list/sources = list() // all radiation source datums - var/list/sources_assoc = list() // Sources indexed by turf for de-duplication. - var/list/resistance_cache = list() // Cache of turf's radiation resistance. - -// Describes a point source of radiation. Created either in response to a pulse of radiation, or over an irradiated atom. -// Sources will decay over time, unless something is renewing their power! -/datum/radiation_source - var/turf/source_turf // Location of the radiation source. - var/rad_power // Strength of the radiation being emitted. - var/decay = TRUE // True for automatic decay. False if owner promises to handle it (i.e. supermatter) - var/respect_maint = FALSE // True for not affecting RAD_SHIELDED areas. - var/flat = FALSE // True for power falloff with distance. - var/range // Cached maximum range, used for quick checks against mobs. - -/datum/radiation_source/Destroy() - radiation_repository.sources -= src - if(radiation_repository.sources_assoc[src.source_turf] == src) - radiation_repository.sources_assoc -= src.source_turf - src.source_turf = null - . = ..() - -/datum/radiation_source/proc/update_rad_power(var/new_power = null) - if(new_power == null || new_power == rad_power) - return // No change - else if(new_power <= 0) - qdel(src) // Decayed to nothing - else - rad_power = new_power - if(!flat) - range = min(round(sqrt(rad_power / config.radiation_lower_limit)), 31) // R = rad_power / dist**2 - Solve for dist - -// Ray trace from all active radiation sources to T and return the strongest effect. -/repository/radiation/proc/get_rads_at_turf(var/turf/T) - if(!istype(T)) return 0 - - . = 0 - for(var/value in sources) - var/datum/radiation_source/source = value - if(source.rad_power < .) - continue // Already being affected by a stronger source - if(source.source_turf.z != T.z) - continue // Radiation is not multi-z - var/dist = get_dist(source.source_turf, T) - if(dist > source.range) - continue // Too far to possibly affect - if(source.respect_maint) - var/atom/A = T.loc - if(A.flags & RAD_SHIELDED) - continue // In shielded area - if(source.flat) - . = max(., source.rad_power) - continue // No need to ray trace for flat field - - // Okay, now ray trace to find resistence! - var/turf/origin = source.source_turf - var/working = source.rad_power - while(origin != T) - origin = get_step_towards(origin, T) //Raytracing - if(!(origin in resistance_cache)) //Only get the resistance if we don't already know it. - origin.calc_rad_resistance() - working = max((working - (origin.cached_rad_resistance * config.radiation_resistance_multiplier)), 0) - if(working <= .) - break // Already affected by a stronger source (or its zero...) - . = max((working * (1 / (dist ** 2))), .) //Butchered version of the inverse square law. Works for this purpose - -// Add a radiation source instance to the repository. It will override any existing source on the same turf. -/repository/radiation/proc/add_source(var/datum/radiation_source/S) - if(!isturf(S.source_turf)) - return - var/datum/radiation_source/existing = sources_assoc[S.source_turf] - if(existing) - qdel(existing) - sources += S - sources_assoc[S.source_turf] = S - -// Creates a temporary radiation source that will decay -/repository/radiation/proc/radiate(source, power) //Sends out a radiation pulse, taking walls into account - if(!(source && power)) //Sanity checking - return - var/datum/radiation_source/S = new() - S.source_turf = get_turf(source) - S.update_rad_power(power) - add_source(S) - -// Sets the radiation in a range to a constant value. -/repository/radiation/proc/flat_radiate(source, power, range, var/respect_maint = TRUE) //VOREStation edit; Respect shielded areas by default please. - if(!(source && power && range)) - return - var/datum/radiation_source/S = new() - S.flat = TRUE - S.range = range - S.respect_maint = respect_maint - S.source_turf = get_turf(source) - S.update_rad_power(power) - add_source(S) - -// Irradiates a full Z-level. Hacky way of doing it, but not too expensive. -/repository/radiation/proc/z_radiate(var/atom/source, power, var/respect_maint = TRUE) //VOREStation edit; Respect shielded areas by default please. - if(!(power && source)) - return - var/turf/epicentre = locate(round(world.maxx / 2), round(world.maxy / 2), source.z) - flat_radiate(epicentre, power, world.maxx, respect_maint) - -/turf - var/cached_rad_resistance = 0 - -/turf/proc/calc_rad_resistance() - cached_rad_resistance = 0 - for(var/obj/O in src.contents) - if(O.rad_resistance) //Override - cached_rad_resistance += O.rad_resistance - - else if(O.density) //So open doors don't get counted - var/material/M = O.get_material() - if(!M) continue - cached_rad_resistance += M.weight + M.radiation_resistance - // Looks like storing the contents length is meant to be a basic check if the cache is stale due to items enter/exiting. Better than nothing so I'm leaving it as is. ~Leshana - radiation_repository.resistance_cache[src] = (length(contents) + 1) - -/turf/simulated/wall/calc_rad_resistance() - radiation_repository.resistance_cache[src] = (length(contents) + 1) - cached_rad_resistance = (density ? material.weight + material.radiation_resistance : 0) - -/obj - var/rad_resistance = 0 // Allow overriding rad resistance - -// If people expand the system, this may be useful. Here as a placeholder until then -/atom/proc/rad_act(var/severity) - return 1 - -/mob/living/rad_act(var/severity) - if(severity && !isbelly(loc)) //eaten mobs are made immune to radiation //VOREStation Edit Start - src.apply_effect(severity, IRRADIATE, src.getarmor(null, "rad")) - for(var/atom/I in src) - I.rad_act(severity) ///VOREStation Edit End diff --git a/code/datums/repositories/unique.dm b/code/datums/repositories/unique.dm new file mode 100644 index 0000000000..a53111cb24 --- /dev/null +++ b/code/datums/repositories/unique.dm @@ -0,0 +1,70 @@ +var/repository/unique/uniqueness_repository = new() + +/repository/unique + var/list/generators + +/repository/unique/New() + ..() + generators = list() + +/repository/unique/proc/Generate() + var/generator_type = args[1] + var/datum/uniqueness_generator/generator = generators[generator_type] + if(!generator) + generator = new generator_type() + generators[generator_type] = generator + var/list/generator_args = args.Copy() // Cannot cut args directly, BYOND complains about it being readonly. + generator_args -= generator_type + return generator.Generate(arglist(generator_args)) + +/datum/uniqueness_generator/proc/Generate() + return + +/datum/uniqueness_generator/id_sequential + var/list/ids_by_key + +/datum/uniqueness_generator/id_sequential/New() + ..() + ids_by_key = list() + +/datum/uniqueness_generator/id_sequential/Generate(var/key, var/default_id = 100) + var/id = ids_by_key[key] + if(id) + id++ + else + id = default_id + + ids_by_key[key] = id + . = id + +/datum/uniqueness_generator/id_random + var/list/ids_by_key + +/datum/uniqueness_generator/id_random/New() + ..() + ids_by_key = list() + +/datum/uniqueness_generator/id_random/Generate(var/key, var/min, var/max) + var/list/ids = ids_by_key[key] + if(!ids) + ids = list() + ids_by_key[key] = ids + + if(ids.len >= (max - min) + 1) + error("Random ID limit reached for key [key].") + ids.Cut() + + if(ids.len >= 0.6 * ((max-min) + 1)) // if more than 60% of possible ids used + . = list() + for(var/i = min to max) + if(i in ids) + continue + . += i + var/id = pick(.) + ids += id + return id + else + do + . = rand(min, max) + while(. in ids) + ids += . diff --git a/code/datums/riding.dm b/code/datums/riding.dm index 746cc6dcf7..4f9513397a 100644 --- a/code/datums/riding.dm +++ b/code/datums/riding.dm @@ -101,7 +101,7 @@ if(keycheck(user)) if(!Process_Spacemove(direction) || !isturf(ridden.loc)) return - step(ridden, direction) + ridden.Move(get_step(ridden, direction)) handle_vehicle_layer() handle_vehicle_offsets() diff --git a/code/datums/supplypacks/contraband.dm b/code/datums/supplypacks/contraband.dm index 339aae9a7c..7b266b2a76 100644 --- a/code/datums/supplypacks/contraband.dm +++ b/code/datums/supplypacks/contraband.dm @@ -88,7 +88,8 @@ /obj/item/weapon/storage/box/syndie_kit/chameleon, /obj/item/device/encryptionkey/syndicate, /obj/item/weapon/card/id/syndicate, - /obj/item/clothing/mask/gas/voice + /obj/item/clothing/mask/gas/voice, + /obj/item/weapon/makeover ), list( //the professional, /obj/item/weapon/gun/projectile/silenced, diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm index e3b9a148cc..e387309da7 100644 --- a/code/datums/supplypacks/engineering.dm +++ b/code/datums/supplypacks/engineering.dm @@ -21,6 +21,20 @@ containertype = /obj/structure/closet/crate/engineering containername = "Superconducting Magnetic Coil crate" +/datum/supply_pack/eng/smescoil/super_capacity + name = "Superconducting Capacitance Coil" + contains = list(/obj/item/weapon/smes_coil/super_capacity) + cost = 90 + containertype = /obj/structure/closet/crate/engineering + containername = "Superconducting Capacitance Coil crate" + +/datum/supply_pack/eng/smescoil/super_io + name = "Superconducting Transmission Coil" + contains = list(/obj/item/weapon/smes_coil/super_io) + cost = 90 + containertype = /obj/structure/closet/crate/engineering + containername = "Superconducting Transmission Coil crate" + /datum/supply_pack/eng/shield_capacitor name = "Shield Capacitor" contains = list(/obj/machinery/shield_capacitor) @@ -290,4 +304,4 @@ cost = 75 containername = "Tritium crate" containertype = /obj/structure/closet/crate/engineering - contains = list(/obj/fiftyspawner/tritium) \ No newline at end of file + contains = list(/obj/fiftyspawner/tritium) diff --git a/code/datums/supplypacks/engineering_vr.dm b/code/datums/supplypacks/engineering_vr.dm index e334268426..273d3f6558 100644 --- a/code/datums/supplypacks/engineering_vr.dm +++ b/code/datums/supplypacks/engineering_vr.dm @@ -4,6 +4,7 @@ cost = 30 containertype = /obj/structure/closet/crate/large containername = "thermal regulator crate" + access = access_atmospherics /datum/supply_pack/eng/radsuit contains = list( diff --git a/code/datums/supplypacks/hospitality_vr.dm b/code/datums/supplypacks/hospitality_vr.dm index 95a921f817..decb7027db 100644 --- a/code/datums/supplypacks/hospitality_vr.dm +++ b/code/datums/supplypacks/hospitality_vr.dm @@ -1,3 +1,6 @@ +/datum/supply_pack/randomised/hospitality/pizza + cost = 50 + /datum/supply_pack/randomised/hospitality/burgers_vr num_contained = 5 contains = list( @@ -79,6 +82,3 @@ cost = 50 containertype = /obj/structure/closet/crate/freezer containername = "Chinese takeout crate" - -/datum/supply_pack/randomised/hospitality/pizza - cost = 25 diff --git a/code/datums/supplypacks/hydroponics_vr.dm b/code/datums/supplypacks/hydroponics_vr.dm index 9ccbdf2d9e..c4003097a7 100644 --- a/code/datums/supplypacks/hydroponics_vr.dm +++ b/code/datums/supplypacks/hydroponics_vr.dm @@ -38,3 +38,14 @@ cost = 60 //considering a corgi crate is 50, and you get two fennecs containertype = /obj/structure/largecrate/animal/fennec containername = "Fennec crate" + +/datum/supply_pack/hydro/fish + name = "Fish supply crate" + contains = list( + /obj/item/weapon/reagent_containers/food/snacks/lobster = 6, + /obj/item/weapon/reagent_containers/food/snacks/cuttlefish = 8, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfish = 1 + ) + cost = 20 + containertype = /obj/structure/closet/crate/freezer + containername = "Fish crate" \ No newline at end of file diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index 555df731c4..554a826f20 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -62,7 +62,7 @@ /obj/item/weapon/surgical/circular_saw ) cost = 25 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Surgery crate" access = access_medical @@ -73,7 +73,7 @@ /obj/item/weapon/storage/box/cdeathalarm_kit ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Death Alarm crate" access = access_medical @@ -83,7 +83,7 @@ /obj/item/weapon/storage/firstaid/clotting ) cost = 100 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Clotting Medicine crate" access = access_medical @@ -97,7 +97,7 @@ /obj/item/weapon/storage/belt/medical = 3 ) cost = 10 - containertype = "/obj/structure/closet/crate" + containertype = /obj/structure/closet/crate containername = "Sterile equipment crate" /datum/supply_pack/med/extragear @@ -109,7 +109,7 @@ /obj/item/clothing/suit/storage/hooded/wintercoat/medical = 3 ) cost = 10 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical surplus equipment" access = access_medical @@ -133,7 +133,7 @@ /obj/item/weapon/reagent_containers/syringe ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Chief medical officer equipment" access = access_cmo @@ -156,7 +156,7 @@ /obj/item/weapon/reagent_containers/syringe ) cost = 20 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical Doctor equipment" access = access_medical_equip @@ -179,7 +179,7 @@ /obj/item/weapon/reagent_containers/syringe ) cost = 20 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Chemist equipment" access = access_chemistry @@ -207,7 +207,7 @@ /obj/item/clothing/accessory/storage/white_vest ) cost = 20 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Paramedic equipment" access = access_medical_equip @@ -226,7 +226,7 @@ /obj/item/weapon/cartridge/medical ) cost = 20 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Psychiatrist equipment" access = access_psychiatrist @@ -247,7 +247,7 @@ /obj/item/weapon/storage/box/gloves ) cost = 10 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical scrubs crate" access = access_medical_equip @@ -264,7 +264,7 @@ /obj/item/weapon/pen ) cost = 20 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Autopsy equipment crate" access = access_morgue @@ -291,7 +291,7 @@ /obj/item/weapon/storage/box/gloves ) cost = 10 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical uniform crate" access = access_medical_equip @@ -309,7 +309,7 @@ /obj/item/weapon/storage/box/gloves ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical biohazard equipment" access = access_medical_equip @@ -317,7 +317,7 @@ name = "Portable freezers crate" contains = list(/obj/item/weapon/storage/box/freezer = 7) cost = 25 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Portable freezers" access = access_medical_equip @@ -325,7 +325,7 @@ name = "Virus sample crate" contains = list(/obj/item/weapon/virusdish/random = 4) cost = 25 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Virus sample crate" access = access_cmo @@ -334,4 +334,39 @@ contains = list(/obj/item/device/defib_kit = 2) cost = 30 containertype = /obj/structure/closet/crate/medical - containername = "Defibrillator crate" \ No newline at end of file + containername = "Defibrillator crate" + +/datum/supply_pack/med/distillery + name = "Chemical distiller crate" + contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery = 1) + cost = 175 + containertype = /obj/structure/largecrate + containername = "Chemical distiller crate" + +/datum/supply_pack/med/advdistillery + name = "Industrial Chemical distiller crate" + contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial = 1) + cost = 250 + containertype = /obj/structure/largecrate + containername = "Industrial Chemical distiller crate" + +/datum/supply_pack/med/oxypump + name = "Oxygen pump crate" + contains = list(/obj/machinery/oxygen_pump/mobile = 1) + cost = 125 + containertype = /obj/structure/largecrate + containername = "Oxygen pump crate" + +/datum/supply_pack/med/anestheticpump + name = "Anesthetic pump crate" + contains = list(/obj/machinery/oxygen_pump/mobile/anesthetic = 1) + cost = 130 + containertype = /obj/structure/largecrate + containername = "Anesthetic pump crate" + +/datum/supply_pack/med/stablepump + name = "Portable stabilizer crate" + contains = list(/obj/machinery/oxygen_pump/mobile/stabilizer = 1) + cost = 175 + containertype = /obj/structure/largecrate + containername = "Portable stabilizer crate" diff --git a/code/datums/supplypacks/medical_vr.dm b/code/datums/supplypacks/medical_vr.dm index 5a080c5e9f..0538f83867 100644 --- a/code/datums/supplypacks/medical_vr.dm +++ b/code/datums/supplypacks/medical_vr.dm @@ -1,38 +1,50 @@ -/datum/supply_pack/med/medicalbiosuits - contains = list( - /obj/item/clothing/head/bio_hood/scientist = 3, - /obj/item/clothing/suit/bio_suit/scientist = 3, - /obj/item/clothing/suit/bio_suit/virology = 3, - /obj/item/clothing/head/bio_hood/virology = 3, - /obj/item/clothing/suit/bio_suit/cmo, - /obj/item/clothing/head/bio_hood/cmo, - /obj/item/clothing/shoes/white = 7, - /obj/item/clothing/mask/gas = 7, - /obj/item/weapon/tank/oxygen = 7, - /obj/item/weapon/storage/box/masks, - /obj/item/weapon/storage/box/gloves - ) - cost = 40 - -/datum/supply_pack/med/virologybiosuits - name = "Virology biohazard gear" - contains = list( - /obj/item/clothing/suit/bio_suit/virology = 3, - /obj/item/clothing/head/bio_hood/virology = 3, - /obj/item/clothing/mask/gas = 3, - /obj/item/weapon/tank/oxygen = 3, - /obj/item/weapon/storage/box/masks, - /obj/item/weapon/storage/box/gloves - ) - cost = 40 - containertype = "/obj/structure/closet/crate/secure" - containername = "Virology biohazard equipment" - access = access_medical_equip - -/datum/supply_pack/med/virus - name = "Virus sample crate" - contains = list(/obj/item/weapon/virusdish/random = 4) - cost = 25 - containertype = "/obj/structure/closet/crate/secure" - containername = "Virus sample crate" - access = access_medical_equip \ No newline at end of file +/datum/supply_pack/med/medicalbiosuits + contains = list( + /obj/item/clothing/head/bio_hood/scientist = 3, + /obj/item/clothing/suit/bio_suit/scientist = 3, + /obj/item/clothing/suit/bio_suit/virology = 3, + /obj/item/clothing/head/bio_hood/virology = 3, + /obj/item/clothing/suit/bio_suit/cmo, + /obj/item/clothing/head/bio_hood/cmo, + /obj/item/clothing/shoes/white = 7, + /obj/item/clothing/mask/gas = 7, + /obj/item/weapon/tank/oxygen = 7, + /obj/item/weapon/storage/box/masks, + /obj/item/weapon/storage/box/gloves + ) + cost = 40 + +/datum/supply_pack/med/virologybiosuits + name = "Virology biohazard gear" + contains = list( + /obj/item/clothing/suit/bio_suit/virology = 3, + /obj/item/clothing/head/bio_hood/virology = 3, + /obj/item/clothing/mask/gas = 3, + /obj/item/weapon/tank/oxygen = 3, + /obj/item/weapon/storage/box/masks, + /obj/item/weapon/storage/box/gloves + ) + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "Virology biohazard equipment" + access = access_medical_equip + +/datum/supply_pack/med/virus + name = "Virus sample crate" + contains = list(/obj/item/weapon/virusdish/random = 4) + cost = 25 + containertype = /obj/structure/closet/crate/secure + containername = "Virus sample crate" + access = access_medical_equip + + +/datum/supply_pack/med/bloodpack + containertype = /obj/structure/closet/crate/medical/blood + +/datum/supply_pack/med/compactdefib + name = "Compact Defibrillator crate" + contains = list(/obj/item/device/defib_kit/compact = 1) + cost = 90 + containertype = /obj/structure/closet/crate/secure + containername = "Compact Defibrillator crate" + access = access_medical_equip diff --git a/code/datums/supplypacks/misc.dm b/code/datums/supplypacks/misc.dm index c4cd4992c1..7f4f0362db 100644 --- a/code/datums/supplypacks/misc.dm +++ b/code/datums/supplypacks/misc.dm @@ -103,7 +103,7 @@ /obj/item/weapon/storage/fancy/candle_box = 3 ) cost = 10 - containertype = "/obj/structure/closet/crate" + containertype = /obj/structure/closet/crate containername = "Chaplain equipment crate" /datum/supply_pack/misc/hoverpod @@ -126,7 +126,7 @@ /obj/item/clothing/accessory/storage/webbing ) cost = 10 - containertype = "/obj/structure/closet/crate" + containertype = /obj/structure/closet/crate containername = "Webbing crate" /datum/supply_pack/misc/holoplant @@ -142,5 +142,40 @@ /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5 ) cost = 25 - containertype = "obj/structure/closet/crate" - containername = "Glucose Hypo Crate" \ No newline at end of file + containertype = /obj/structure/closet/crate + containername = "Glucose Hypo Crate" + +/datum/supply_pack/misc/mre_rations + num_contained = 6 + name = "Emergency - MREs" + contains = list(/obj/item/weapon/storage/mre, + /obj/item/weapon/storage/mre/menu2, + /obj/item/weapon/storage/mre/menu3, + /obj/item/weapon/storage/mre/menu4, + /obj/item/weapon/storage/mre/menu5, + /obj/item/weapon/storage/mre/menu6, + /obj/item/weapon/storage/mre/menu7, + /obj/item/weapon/storage/mre/menu8, + /obj/item/weapon/storage/mre/menu9, + /obj/item/weapon/storage/mre/menu10) + cost = 50 + containertype = /obj/structure/closet/crate/freezer + containername = "ready to eat rations" + +/datum/supply_pack/misc/paste_rations + name = "Emergency - Paste" + contains = list( + /obj/item/weapon/storage/mre/menu11 = 2 + ) + cost = 25 + containertype = /obj/structure/closet/crate/freezer + containername = "emergency rations" + +/datum/supply_pack/misc/medical_rations + name = "Emergency - VitaPaste" + contains = list( + /obj/item/weapon/storage/mre/menu13 = 2 + ) + cost = 40 + containertype = /obj/structure/closet/crate/freezer + containername = "emergency rations" diff --git a/code/datums/supplypacks/misc_vr.dm b/code/datums/supplypacks/misc_vr.dm index 973840f5b2..93523707b9 100644 --- a/code/datums/supplypacks/misc_vr.dm +++ b/code/datums/supplypacks/misc_vr.dm @@ -13,24 +13,6 @@ containername = "Belt-miner gear crate" access = access_mining -/datum/supply_pack/misc/rations - name = "Emergency rations" - contains = list( - /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 4, - ) - cost = 20 - containertype = /obj/structure/closet/crate/freezer - containername = "emergency rations" - -/datum/supply_pack/misc/proteinrations - name = "Emergency meat rations" - contains = list( - /obj/item/weapon/reagent_containers/food/snacks/liquidprotein = 4, - ) - cost = 30 - containertype = /obj/structure/closet/crate/freezer - containername = "emergency meat rations" - /datum/supply_pack/misc/eva_rig name = "eva hardsuit (empty)" contains = list( @@ -39,7 +21,11 @@ cost = 150 containertype = /obj/structure/closet/crate/secure/gear containername = "eva hardsuit crate" - access = access_mining + access = list(access_mining, + access_eva, + access_explorer, + access_pilot) + one_access = TRUE /datum/supply_pack/misc/mining_rig name = "industrial hardsuit (empty)" @@ -49,4 +35,46 @@ cost = 150 containertype = /obj/structure/closet/crate/secure/gear containername = "industrial hardsuit crate" - access = access_mining \ No newline at end of file + access = list(access_mining, + access_eva) + one_access = TRUE + +/datum/supply_pack/misc/medical_rig + name = "medical hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/medical = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "medical hardsuit crate" + access = access_medical + +/datum/supply_pack/misc/security_rig + name = "hazard hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/hazard = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "hazard hardsuit crate" + access = access_armory + +/datum/supply_pack/misc/science_rig + name = "ami hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/hazmat = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "ami hardsuit crate" + access = access_rd + +/datum/supply_pack/misc/ce_rig + name = "advanced voidsuit (empty)" + contains = list( + /obj/item/weapon/rig/ce = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "advanced voidsuit crate" + access = access_ce diff --git a/code/datums/supplypacks/recreation.dm b/code/datums/supplypacks/recreation.dm index 684ef962f4..357f0eaead 100644 --- a/code/datums/supplypacks/recreation.dm +++ b/code/datums/supplypacks/recreation.dm @@ -55,7 +55,7 @@ /obj/item/weapon/wrapping_paper = 3 ) cost = 10 - containertype = "/obj/structure/closet/crate" + containertype = /obj/structure/closet/crate containername = "Arts and Crafts crate" /datum/supply_pack/recreation/painters @@ -67,4 +67,34 @@ /obj/item/device/pipe_painter = 2, /obj/item/device/floor_painter = 2, /obj/item/device/closet_painter = 2 + ) + +/datum/supply_pack/recreation/cheapbait + name = "Cheap Fishing Bait" + cost = 10 + containername = "cheap bait crate" + containertype = /obj/structure/closet/crate/freezer + contains = list( + /obj/item/weapon/storage/box/wormcan/sickly = 5 + ) + +/datum/supply_pack/randomised/recreation/cheapbait + name = "Deluxe Fishing Bait" + cost = 40 + containername = "deluxe bait crate" + containertype = /obj/structure/closet/crate/freezer + num_contained = 8 + contains = list( + /obj/item/weapon/storage/box/wormcan, + /obj/item/weapon/storage/box/wormcan/deluxe + ) + +/datum/supply_pack/recreation/ltagturrets + name = "Laser Tag Turrets" + cost = 40 + containername = "laser tag turret crate" + containertype = /obj/structure/closet/crate + contains = list( + /obj/machinery/porta_turret/lasertag/blue, + /obj/machinery/porta_turret/lasertag/red ) \ No newline at end of file diff --git a/code/datums/supplypacks/recreation_vr.dm b/code/datums/supplypacks/recreation_vr.dm index c4d7ee0ccc..2e61f93473 100644 --- a/code/datums/supplypacks/recreation_vr.dm +++ b/code/datums/supplypacks/recreation_vr.dm @@ -40,11 +40,11 @@ containertype = /obj/structure/closet/crate containername = "wolfgirl cosplay crate" -/datum/supply_pack/randomised/recreation/figures_vr +/datum/supply_pack/randomised/recreation/figures name = "Action figures crate" num_contained = 5 contains = list( - /obj/random/action_figure + /obj/random/action_figure/supplypack ) cost = 200 containertype = /obj/structure/closet/crate diff --git a/code/datums/supplypacks/science.dm b/code/datums/supplypacks/science.dm index 83407c2866..ab55b8571e 100644 --- a/code/datums/supplypacks/science.dm +++ b/code/datums/supplypacks/science.dm @@ -56,3 +56,25 @@ cost = 30 containertype = /obj/structure/closet/crate containername = "Integrated circuit crate" + +/datum/supply_pack/sci/xenoarch + name = "Xenoarchaeology Tech crate" + contains = list( + /obj/item/weapon/pickaxe/excavationdrill, + /obj/item/device/xenoarch_multi_tool, + /obj/item/clothing/suit/space/anomaly, + /obj/item/clothing/head/helmet/space/anomaly, + /obj/item/weapon/storage/belt/archaeology, + /obj/item/device/flashlight/lantern, + /obj/item/device/core_sampler, + /obj/item/device/gps, + /obj/item/device/beacon_locator, + /obj/item/device/radio/beacon, + /obj/item/clothing/glasses/meson, + /obj/item/weapon/pickaxe, + /obj/item/weapon/storage/bag/fossils, + /obj/item/weapon/hand_labeler) + cost = 100 + containertype = /obj/structure/closet/crate/secure/science + containername = "Xenoarchaeology Tech crate" + access = access_research \ No newline at end of file diff --git a/code/datums/supplypacks/security.dm b/code/datums/supplypacks/security.dm index 40698def5d..6e9e82bdde 100644 --- a/code/datums/supplypacks/security.dm +++ b/code/datums/supplypacks/security.dm @@ -31,7 +31,6 @@ cost = 40 containertype = /obj/structure/closet/crate/secure/gear containername = "Armor crate" - access_armory //VOREStation Add - Armor is for the armory. /datum/supply_pack/security/riot_gear name = "Gear - Riot" @@ -125,6 +124,30 @@ /obj/item/clothing/gloves/black ) */ + +/datum/supply_pack/security/flexitac + name = "Armor - Tactical Light" + containertype = /obj/structure/closet/crate/secure/gear + containername = "Tactical Light armor crate" + cost = 75 + access = access_armory + contains = list( + /obj/item/clothing/suit/storage/vest/heavy/flexitac, + /obj/item/clothing/head/helmet/flexitac, + /obj/item/clothing/shoes/leg_guard/flexitac, + /obj/item/clothing/gloves/arm_guard/flexitac, + /obj/item/clothing/mask/balaclava/tactical, + /obj/item/clothing/glasses/sunglasses/sechud/tactical, + /obj/item/weapon/storage/belt/security/tactical, + /obj/item/clothing/suit/storage/vest/heavy/flexitac, + /obj/item/clothing/head/helmet/flexitac, + /obj/item/clothing/shoes/leg_guard/flexitac, + /obj/item/clothing/gloves/arm_guard/flexitac, + /obj/item/clothing/mask/balaclava/tactical, + /obj/item/clothing/glasses/sunglasses/sechud/tactical, + /obj/item/weapon/storage/belt/security/tactical + ) + /datum/supply_pack/security/securitybarriers name = "Misc - Security Barriers" contains = list(/obj/machinery/deployable/barrier = 4) @@ -373,4 +396,4 @@ cost = 25 containertype = /obj/structure/closet/crate/secure containername = "Security biohazard gear" - access = access_security \ No newline at end of file + access = access_security diff --git a/code/datums/supplypacks/security_vr.dm b/code/datums/supplypacks/security_vr.dm index 3538a311d5..a00556e528 100644 --- a/code/datums/supplypacks/security_vr.dm +++ b/code/datums/supplypacks/security_vr.dm @@ -6,6 +6,7 @@ access = list( access_security, access_xenobiology) + one_access = TRUE /datum/supply_pack/security/guardmutant name = "VARMAcorp autoNOMous security solution for hostile environments" @@ -15,8 +16,12 @@ access = list( access_security, access_xenobiology) + one_access = TRUE */ +/datum/supply_pack/randomised/security/armor + access = access_armory + /datum/supply_pack/security/biosuit contains = list( /obj/item/clothing/head/bio_hood/security = 3, @@ -29,3 +34,23 @@ /obj/item/weapon/storage/box/gloves ) cost = 40 + +/datum/supply_pack/security/trackingimplant + name = "Implants - Tracking" + contains = list( + /obj/item/weapon/storage/box/trackimp = 1 + ) + cost = 25 + containertype = /obj/structure/closet/crate/secure + containername = "Tracking implants" + access = access_security + +/datum/supply_pack/security/chemicalimplant + name = "Implants - Chemical" + contains = list( + /obj/item/weapon/storage/box/chemimp = 1 + ) + cost = 25 + containertype = /obj/structure/closet/crate/secure + containername = "Chemical implants" + access = access_security diff --git a/code/datums/supplypacks/supply.dm b/code/datums/supplypacks/supply.dm index e8f26993ff..c0db598cc4 100644 --- a/code/datums/supplypacks/supply.dm +++ b/code/datums/supplypacks/supply.dm @@ -61,7 +61,7 @@ /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tape_roll = 2) cost = 10 - containertype = "/obj/structure/closet/crate" + containertype = /obj/structure/closet/crate containername = "Shipping supplies crate" /datum/supply_pack/supply/bureaucracy @@ -111,7 +111,7 @@ /obj/item/clothing/glasses/meson ) cost = 10 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Shaft miner equipment" access = access_mining /* //VOREStation Edit - Pointless on Tether. diff --git a/code/datums/supplypacks/supplypacks.dm b/code/datums/supplypacks/supplypacks.dm index d29f03be6f..d419198af3 100644 --- a/code/datums/supplypacks/supplypacks.dm +++ b/code/datums/supplypacks/supplypacks.dm @@ -33,6 +33,7 @@ var/list/all_supply_groups = list("Atmospherics", var/containertype = null var/containername = null var/access = null + var/one_access = FALSE var/contraband = 0 var/num_contained = 0 //number of items picked to be contained in a /randomised crate var/group = "Miscellaneous" diff --git a/code/datums/supplypacks/voidsuits.dm b/code/datums/supplypacks/voidsuits.dm index 1feb6fe92e..22bff55fcf 100644 --- a/code/datums/supplypacks/voidsuits.dm +++ b/code/datums/supplypacks/voidsuits.dm @@ -17,7 +17,7 @@ /obj/item/weapon/tank/oxygen = 2, ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Atmospheric voidsuit crate" access = access_atmospherics @@ -31,7 +31,7 @@ /obj/item/weapon/tank/oxygen = 2, ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Heavy Duty Atmospheric voidsuit crate" access = access_atmospherics @@ -45,7 +45,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Engineering voidsuit crate" access = access_engine_equip @@ -59,7 +59,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Engineering Construction voidsuit crate" access = access_engine_equip @@ -73,7 +73,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 45 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Engineering Hazmat voidsuit crate" access = access_engine_equip @@ -87,7 +87,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Reinforced Engineering voidsuit crate" access = access_engine_equip @@ -101,7 +101,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical voidsuit crate" access = access_medical_equip @@ -115,7 +115,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical EMT voidsuit crate" access = access_medical_equip @@ -129,7 +129,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 45 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Medical Biohazard voidsuit crate" access = access_medical_equip @@ -143,7 +143,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 60 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Vey-Med Medical voidsuit crate" access = access_medical_equip @@ -157,7 +157,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Security voidsuit crate" /datum/supply_pack/voidsuits/security/crowd @@ -170,7 +170,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Security Crowd Control voidsuit crate" access = access_armory @@ -184,7 +184,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Security EVA Riot voidsuit crate" access = access_armory @@ -197,7 +197,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 40 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Mining voidsuit crate" access = access_mining @@ -210,7 +210,7 @@ /obj/item/weapon/tank/oxygen = 2 ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Frontier Mining voidsuit crate" access = access_mining @@ -221,6 +221,6 @@ /obj/item/clothing/mask/gas/zaddat = 1 ) cost = 30 - containertype = "/obj/structure/closet/crate" + containertype = /obj/structure/closet/crate containername = "Zaddat Shroud crate" access = null \ No newline at end of file diff --git a/code/datums/supplypacks/voidsuits_vr.dm b/code/datums/supplypacks/voidsuits_vr.dm index ccf296a10a..cfadfcf213 100644 --- a/code/datums/supplypacks/voidsuits_vr.dm +++ b/code/datums/supplypacks/voidsuits_vr.dm @@ -62,7 +62,6 @@ ) /datum/supply_pack/voidsuits/supply - name = "Mining voidsuits" contains = list( /obj/item/clothing/suit/space/void/mining = 3, /obj/item/clothing/head/helmet/space/void/mining = 3, @@ -80,7 +79,7 @@ /obj/item/weapon/tank/oxygen = 3 ) cost = 50 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Exploration voidsuit crate" access = access_explorer @@ -94,6 +93,6 @@ /obj/item/weapon/tank/oxygen = 1 ) cost = 20 - containertype = "/obj/structure/closet/crate/secure" + containertype = /obj/structure/closet/crate/secure containername = "Pilot voidsuit crate" access = access_pilot \ No newline at end of file diff --git a/code/datums/uplink/implants.dm b/code/datums/uplink/implants.dm index 7e3e2f3005..e36396c9cd 100644 --- a/code/datums/uplink/implants.dm +++ b/code/datums/uplink/implants.dm @@ -23,3 +23,53 @@ name = "Uplink Implant" //Original name: "Uplink Implant (Contains 5 Telecrystals)" item_cost = 50 //Original cost: 10 path = /obj/item/weapon/storage/box/syndie_kit/imp_uplink + +/datum/uplink_item/item/implants/imp_shades + name = "Integrated Thermal-Shades Implant (Organic)" + item_cost = 80 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug + +/datum/uplink_item/item/implants/imp_taser + name = "Integrated Taser Implant (Organic)" + item_cost = 30 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/taser + +/datum/uplink_item/item/implants/imp_laser + name = "Integrated Laser Implant (Organic)" + item_cost = 50 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/laser + +/datum/uplink_item/item/implants/imp_dart + name = "Integrated Dart Implant (Organic)" + item_cost = 60 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/dart + +/datum/uplink_item/item/implants/imp_toolkit + name = "Integrated Toolkit Implant (Organic)" + item_cost = 80 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/toolkit + +/datum/uplink_item/item/implants/imp_medkit + name = "Integrated Medkit Implant (Organic)" + item_cost = 60 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/medkit + +/datum/uplink_item/item/implants/imp_analyzer + name = "Integrated Research Scanner Implant (Organic)" + item_cost = 20 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/analyzer + +/datum/uplink_item/item/implants/imp_sword + name = "Integrated Sword Implant (Organic)" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/sword + +/datum/uplink_item/item/implants/imp_sprinter + name = "Integrated Sprinter Implant (Organic)" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/sprinter + +/datum/uplink_item/item/implants/imp_sprinter + name = "Integrated Surge Implant (Organic)" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/surge diff --git a/code/datums/uplink/medical_vr.dm b/code/datums/uplink/medical_vr.dm new file mode 100644 index 0000000000..324398843f --- /dev/null +++ b/code/datums/uplink/medical_vr.dm @@ -0,0 +1,57 @@ +/********** +* Medical * +**********/ +/datum/uplink_item/item/medical/mre + name = "Meal, Ready to eat (Random)" + item_cost = 5 + path = /obj/item/weapon/storage/mre/random + +/datum/uplink_item/item/medical/protein + name = "Meal, Ready to eat (Protein)" + item_cost = 5 + path = /obj/item/weapon/storage/mre/menu10 + +/datum/uplink_item/item/medical/emergency + name = "Meal, Ready to eat (Emergency)" + item_cost = 5 + path = /obj/item/weapon/storage/mre/menu11 + +/datum/uplink_item/item/medical/glucose + name = "Glucose injector" + item_cost = 5 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose + +/datum/uplink_item/item/medical/purity + name = "Purity injector" + item_cost = 5 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity + +/datum/uplink_item/item/medical/brute + name = "Brute injector" + item_cost = 5 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute + +/datum/uplink_item/item/medical/burn + name = "Burn injector" + item_cost = 5 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn + +/datum/uplink_item/item/medical/toxin + name = "Toxin injector" + item_cost = 5 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin + +/datum/uplink_item/item/medical/oxy + name = "Oxy injector" + item_cost = 5 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy + +/datum/uplink_item/item/medical/nanites + name = "Healing Nanite pill bottle" + item_cost = 30 + path = /obj/item/weapon/storage/pill_bottle/healing_nanites + +/datum/uplink_item/item/medical/insiderepair + name = "Combat organ kit" + item_cost = 120 + path = /obj/item/weapon/storage/firstaid/insiderepair diff --git a/code/datums/uplink/stealth_items.dm b/code/datums/uplink/stealth_items.dm index 23e0593cad..0a17e659a6 100644 --- a/code/datums/uplink/stealth_items.dm +++ b/code/datums/uplink/stealth_items.dm @@ -34,7 +34,7 @@ item_cost = 15 path = /obj/item/clothing/mask/gas/voice -/datum/uplink_item/item/stealth_items/camera_floppy - name = "Camera Network Access - Floppy" - item_cost = 15 - path = /obj/item/weapon/disk/file/cameras/syndicate +/datum/uplink_item/item/stealth_items/makeover + name = "Makeover Kit" + item_cost = 5 + path = /obj/item/weapon/makeover \ No newline at end of file diff --git a/code/datums/uplink/tools_vr.dm b/code/datums/uplink/tools_vr.dm new file mode 100644 index 0000000000..5a165f7aac --- /dev/null +++ b/code/datums/uplink/tools_vr.dm @@ -0,0 +1,42 @@ +/******************** +* Devices and Tools * +********************/ +/datum/uplink_item/item/tools/basiclaptop + name = "Laptop (Basic)" + item_cost = 5 + path = /obj/item/modular_computer/laptop/preset/custom_loadout/cheap + +/datum/uplink_item/item/tools/survivalcapsule + name = "Survival Capsule" + item_cost = 5 + path = /obj/item/device/survivalcapsule + +/datum/uplink_item/item/tools/nanopaste + name = "Nanopaste (Advanced)" + item_cost = 10 + path = /obj/item/stack/nanopaste/advanced + +/datum/uplink_item/item/tools/elitetablet + name = "Tablet (Advanced)" + item_cost = 15 + path = /obj/item/modular_computer/tablet/preset/custom_loadout/advanced + +/datum/uplink_item/item/tools/elitelaptop + name = "Laptop (Advanced)" + item_cost = 20 + path = /obj/item/modular_computer/laptop/preset/custom_loadout/elite + +/datum/uplink_item/item/tools/luxurycapsule + name = "Survival Capsule (Luxury)" + item_cost = 40 + path = /obj/item/device/survivalcapsule/luxury + +/datum/uplink_item/item/tools/translocator + name = "Translocator" + item_cost = 40 + path = /obj/item/device/perfect_tele + +/datum/uplink_item/item/tools/barcapsule + name = "Survival Capsule (Bar)" + item_cost = 80 + path = /obj/item/device/survivalcapsule/luxurybar diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index 35bb07cbc5..1724b9469c 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -57,9 +57,6 @@ var/const/CAMERA_WIRE_NOTHING2 = 32 var/new_range = (C.view_range == initial(C.view_range) ? C.short_range : initial(C.view_range)) C.setViewRange(new_range) - if(CAMERA_WIRE_POWER) - C.kick_viewers() // Kicks anyone watching the camera - if(CAMERA_WIRE_LIGHT) C.light_disabled = !C.light_disabled diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 6a1e96e18a..3f48c656ba 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -39,7 +39,6 @@ var/const/BORG_WIRE_CAMERA = 16 if (BORG_WIRE_CAMERA) if(!isnull(R.camera) && !R.scrambledcodes) R.camera.status = mended - R.camera.kick_viewers() // Will kick anyone who is watching the Cyborg's camera. if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh if (R.lawupdate) @@ -59,7 +58,6 @@ var/const/BORG_WIRE_CAMERA = 16 if (BORG_WIRE_CAMERA) if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes) - R.camera.kick_viewers() // Kick anyone watching the Cyborg's camera R.visible_message("[R]'s camera lense focuses loudly.") R << "Your camera lense focuses loudly." diff --git a/code/defines/obj.dm b/code/defines/obj.dm index 479a651867..ee2ddea1c0 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -157,7 +157,7 @@ var/global/list/PDA_Manifest = list() list("cat" = "Medical", "elems" = med), list("cat" = "Science", "elems" = sci), list("cat" = "Cargo", "elems" = car), - // list("cat" = "Planetside", "elems" = pla), // VOREStation Edit - Don't show empty dpt in PDA + list("cat" = "Exploration", "elems" = pla), // VOREStation Edit list("cat" = "Civilian", "elems" = civ), list("cat" = "Silicon", "elems" = bot), list("cat" = "Miscellaneous", "elems" = misc) diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index 5bd34e704b..482598027a 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -29,6 +29,7 @@ gender = PLURAL icon = 'icons/obj/items.dmi' icon_state = "soap" + flags = NOCONDUCT w_class = ITEMSIZE_SMALL slot_flags = SLOT_HOLSTER throwforce = 0 @@ -395,7 +396,7 @@ display_contents_with_number = 1 max_w_class = ITEMSIZE_NORMAL max_storage_space = 100 - + /obj/item/weapon/storage/part_replacer/adv name = "advanced rapid part exchange device" desc = "Special mechanical module made to store, sort, and apply standard machine parts. This one has a greatly upgraded storage capacity" @@ -718,7 +719,7 @@ desc = "Instant research tool. For testing purposes only." icon = 'icons/obj/stock_parts.dmi' icon_state = "smes_coil" - origin_tech = list(TECH_MATERIAL = 19, TECH_ENGINEERING = 19, TECH_PHORON = 19, TECH_POWER = 19, TECH_BLUESPACE = 19, TECH_BIO = 19, TECH_COMBAT = 19, TECH_MAGNET = 19, TECH_DATA = 19, TECH_ILLEGAL = 19, TECH_ARCANE = 19) + origin_tech = list(TECH_MATERIAL = 19, TECH_ENGINEERING = 19, TECH_PHORON = 19, TECH_POWER = 19, TECH_BLUESPACE = 19, TECH_BIO = 19, TECH_COMBAT = 19, TECH_MAGNET = 19, TECH_DATA = 19, TECH_ILLEGAL = 19, TECH_ARCANE = 19, TECH_PRECURSOR = 19) // Additional construction stock parts diff --git a/code/game/antagonist/station/highlander.dm b/code/game/antagonist/station/highlander.dm index e2ec983818..f483b1df3b 100644 --- a/code/game/antagonist/station/highlander.dm +++ b/code/game/antagonist/station/highlander.dm @@ -47,8 +47,8 @@ var/datum/antagonist/highlander/highlanders var/obj/item/weapon/card/id/W = new(player) W.name = "[player.real_name]'s ID Card" W.icon_state = "centcom" - W.access = get_all_station_access() - W.access += get_all_centcom_access() + W.access = get_all_station_access().Copy + W.access |= get_all_centcom_access() W.assignment = "Highlander" W.registered_name = player.real_name player.equip_to_slot_or_del(W, slot_wear_id) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 787cc55f7b..ac11cd2e4f 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -2737,4 +2737,4 @@ var/list/the_station_areas = list ( icon_state = "yellow" luminosity = 1 dynamic_lighting = 0 - requires_power = 0 + requires_power = 0 \ No newline at end of file diff --git a/code/game/area/Space Station 13 areas_vr.dm b/code/game/area/Space Station 13 areas_vr.dm index ecca320658..c03489b307 100644 --- a/code/game/area/Space Station 13 areas_vr.dm +++ b/code/game/area/Space Station 13 areas_vr.dm @@ -1,121 +1,4 @@ -/area/crew_quarters/sleep/vistor_room_1 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_2 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_3 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_4 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_5 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_6 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_7 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_8 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_9 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_10 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_11 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/vistor_room_12 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_1 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_2 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_3 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_4 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_5 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_6 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_7 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_8 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_9 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/crew_quarters/sleep/Dorm_10 - flags = RAD_SHIELDED | BLUE_SHIELDED - -/area/teleporter/departing - name = "\improper Long-Range Teleporter" - icon_state = "teleporter" - music = "signal" - -// Override telescience shielding on some areas -/area/security/armoury - flags = BLUE_SHIELDED - -/area/security/tactical - flags = BLUE_SHIELDED - -/area/security/nuke_storage - flags = BLUE_SHIELDED - -/area/supply - flags = BLUE_SHIELDED - -// Add rad shielding to maintenance and construction sites -/area/vacant - flags = RAD_SHIELDED - -/area/maintenance - flags = RAD_SHIELDED - -/area/rnd/research_storage //Located entirely in maint under public access, so why not that too - flags = RAD_SHIELDED - -// New shuttles -/area/shuttle/administration/transit - name = "Deep Space (AS)" - icon_state = "shuttle" - -/area/shuttle/administration/away_mission - name = "Away Mission (AS)" - icon_state = "shuttle" - -/area/shuttle/awaymission/home - name = "NSB Adephagia (AM)" - icon_state = "shuttle2" - -/area/shuttle/awaymission/warp - name = "Deep Space (AM)" - icon_state = "shuttle" - -/area/shuttle/awaymission/away - name = "Away Mission (AM)" - icon_state = "shuttle2" - -/area/shuttle/awaymission/oldengbase - name = "Old Construction Site (AM)" - icon_state = "shuttle2" +//TFF 28/8/19 - cleanup of areas placement - removes all but rogueminer_vr stuff. /area/shuttle/belter/station name = "Belter Shuttle Landed" @@ -162,136 +45,6 @@ icon_state = "red2" shuttle_area = /area/shuttle/belter/belt/zone4 -/area/medical/resleeving - name = "Resleeving Lab" - icon_state = "genetics" - -/area/bigship - name = "Bigship" - requires_power = 0 - flags = RAD_SHIELDED - sound_env = SMALL_ENCLOSED - base_turf = /turf/space - icon_state = "red2" - -/area/bigship/teleporter - name = "Bigship Teleporter Room" - -//////// Small Cruiser Areas //////// -/area/houseboat - name = "Small Cruiser" - requires_power = 0 - flags = RAD_SHIELDED - base_turf = /turf/space - icon_state = "red2" - lightswitch = TRUE - -/area/houseboat/holodeck_area - name = "Small Cruiser - Holodeck" - icon_state = "blue2" - -/area/houseboat/holodeck/off - name = "Small Cruiser Holo - Off" - icon_state = "blue2" -/area/houseboat/holodeck/beach - name = "Small Cruiser Holo - Beach" - icon_state = "blue2" -/area/houseboat/holodeck/snow - name = "Small Cruiser Holo - Snow" - icon_state = "blue2" -/area/houseboat/holodeck/desert - name = "Small Cruiser Holo - Desert" - icon_state = "blue2" -/area/houseboat/holodeck/picnic - name = "Small Cruiser Holo - Picnic" - icon_state = "blue2" -/area/houseboat/holodeck/thunderdome - name = "Small Cruiser Holo - Thunderdome" - icon_state = "blue2" -/area/houseboat/holodeck/basketball - name = "Small Cruiser Holo - Basketball" - icon_state = "blue2" -/area/houseboat/holodeck/gaming - name = "Small Cruiser Holo - Gaming Table" - icon_state = "blue2" -/area/houseboat/holodeck/space - name = "Small Cruiser Holo - Space" - icon_state = "blue2" -/area/houseboat/holodeck/bunking - name = "Small Cruiser Holo - Bunking" - icon_state = "blue2" - -/area/shuttle/cruiser/cruiser - name = "Small Cruiser Shuttle - Cruiser" - icon_state = "blue2" - base_turf = /turf/simulated/floor/tiled/techfloor -/area/shuttle/cruiser/station - name = "Small Cruiser Shuttle - Station" - icon_state = "blue2" - - -// Tether Map has this shuttle -/area/shuttle/tether/surface - name = "Tether Shuttle Landed" - icon_state = "shuttle" - base_turf = /turf/simulated/floor/reinforced - -/area/shuttle/tether/station - name = "Tether Shuttle Dock" - icon_state = "shuttle2" - -/area/shuttle/tether/transit - name = "Tether Shuttle Transit" - icon_state = "shuttle2" - -// rnd (Research and Development) -/area/rnd/research/testingrange - name = "\improper Weapons Testing Range" - icon_state = "firingrange" - -/area/rnd/research/researchdivision - name = "\improper Research Division" - icon_state = "research" - -/area/rnd/outpost - name = "\improper Research Outpost Hallway" - icon_state = "research" - -/area/rnd/outpost/airlock - name = "\improper Research Outpost Airlock" - icon_state = "green" - -/area/rnd/outpost/eva - name = "Research Outpost EVA Storage" - icon_state = "eva" - -/area/rnd/outpost/chamber - name = "\improper Research Outpost Burn Chamber" - icon_state = "engine" - -/area/rnd/outpost/atmos - name = "Research Outpost Atmospherics" - icon_state = "atmos" - -/area/rnd/outpost/storage - name = "\improper Research Outpost Gas Storage" - icon_state = "toxstorage" - -/area/rnd/outpost/mixing - name = "\improper Research Outpost Gas Mixing" - icon_state = "toxmix" - -/area/rnd/outpost/heating - name = "\improper Research Outpost Gas Heating" - icon_state = "toxmix" - -/area/rnd/outpost/testing - name = "\improper Research Outpost Testing" - icon_state = "toxtest" - -/area/maintenance/substation/outpost - name = "Research Outpost Substation" - /area/engineering/engine_gas name = "\improper Engine Gas Storage" icon_state = "engine_waste" diff --git a/code/game/area/areas_vr.dm b/code/game/area/areas_vr.dm new file mode 100644 index 0000000000..d422c1cd0f --- /dev/null +++ b/code/game/area/areas_vr.dm @@ -0,0 +1,6 @@ +/area/shuttle_arrived() + .=..() + for(var/obj/machinery/telecomms/relay/R in contents) + R.reset_z() + for(var/obj/machinery/power/apc/A in contents) + A.update_area() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 6929943de2..631ed4df19 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -388,7 +388,8 @@ blood_DNA = list() was_bloodied = 1 - blood_color = "#A10808" + if(!blood_color) + blood_color = "#A10808" if(istype(M)) if (!istype(M.dna, /datum/dna)) M.dna = new /datum/dna(null) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 104fa066b6..c8f0b44c3d 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,6 +1,6 @@ /atom/movable layer = OBJ_LAYER - appearance_flags = TILE_BOUND|PIXEL_SCALE + appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER //VOREStation Edit var/last_move = null var/anchored = 0 // var/elevation = 2 - not used anywhere @@ -16,7 +16,8 @@ var/moved_recently = 0 var/mob/pulledby = null var/item_state = null // Used to specify the item state for the on-mob overlays. - var/icon_scale = 1 // Used to scale icons up or down in update_transform(). + var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform(). + var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform(). var/icon_rotation = 0 // Used to rotate icons in update_transform() var/old_x = 0 var/old_y = 0 @@ -67,40 +68,51 @@ if(!newloc.Enter(src, src.loc)) return + + if(!check_multi_tile_move_density_dir(direct, locs)) // We're big, and we can't move that way. + return + // Past this is the point of no return - var/atom/oldloc = loc - var/area/oldarea = get_area(oldloc) - var/area/newarea = get_area(newloc) - loc = newloc - . = TRUE - oldloc.Exited(src, newloc) - if(oldarea != newarea) - oldarea.Exited(src, newloc) + if(!locs || locs.len <= 1) // We're not a multi-tile object. + var/atom/oldloc = loc + var/area/oldarea = get_area(oldloc) + var/area/newarea = get_area(newloc) + loc = newloc + . = TRUE + oldloc.Exited(src, newloc) + if(oldarea != newarea) + oldarea.Exited(src, newloc) - for(var/i in oldloc) - if(i == src) // Multi tile objects - continue - var/atom/movable/thing = i - thing.Uncrossed(src) + for(var/i in oldloc) + if(i == src) // Multi tile objects + continue + var/atom/movable/thing = i + thing.Uncrossed(src) - newloc.Entered(src, oldloc) - if(oldarea != newarea) - newarea.Entered(src, oldloc) + newloc.Entered(src, oldloc) + if(oldarea != newarea) + newarea.Entered(src, oldloc) + + for(var/i in loc) + if(i == src) // Multi tile objects + continue + var/atom/movable/thing = i + thing.Crossed(src) + + else if(newloc) // We're a multi-tile object. + . = doMove(newloc) - for(var/i in loc) - if(i == src) // Multi tile objects - continue - var/atom/movable/thing = i - thing.Crossed(src) // //////////////////////////////////////// -/atom/movable/Move(atom/newloc, direct) +/atom/movable/Move(atom/newloc, direct = 0) if(!loc || !newloc) return FALSE var/atom/oldloc = loc if(loc != newloc) + if(!direct) + direct = get_dir(oldloc, newloc) if (!(direct & (direct - 1))) //Cardinal move . = ..() else //Diagonal move, split it into cardinal moves @@ -196,6 +208,13 @@ . = TRUE return CanPass(AM, loc) +/atom/movable/CanPass(atom/movable/mover, turf/target) + . = ..() + if(locs && locs.len >= 2) // If something is standing on top of us, let them pass. + if(mover.loc in locs) + . = TRUE + return . + //oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called! /atom/movable/Crossed(atom/movable/AM, oldloc) return @@ -476,13 +495,18 @@ /atom/movable/proc/update_transform() var/matrix/M = matrix() - M.Scale(icon_scale) + M.Scale(icon_scale_x, icon_scale_y) M.Turn(icon_rotation) src.transform = M // Use this to set the object's scale. -/atom/movable/proc/adjust_scale(new_scale) - icon_scale = new_scale +/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y) + if(isnull(new_scale_y)) + new_scale_y = new_scale_x + if(new_scale_x != 0) + icon_scale_x = new_scale_x + if(new_scale_y != 0) + icon_scale_y = new_scale_y update_transform() /atom/movable/proc/adjust_rotation(new_rotation) diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index b5a73224fc..23d8c7b60f 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -111,6 +111,8 @@ var/global/list/datum/dna/gene/dna_genes[0] // New stuff var/species = SPECIES_HUMAN var/list/body_markings = list() + var/list/body_descriptors = null + var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance. // Make a copy of this strand. // USE THIS WHEN COPYING STUFF OR YOU'LL GET CORRUPTION! diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 998b712828..1fe5d43a59 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -18,6 +18,8 @@ var/mind=null var/languages=null var/list/flavor=null + var/gender = null + var/list/body_descriptors = null var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance. /datum/dna2/record/proc/GetData() @@ -706,7 +708,10 @@ databuf.types = DNA2_BUF_UE databuf.dna = src.connected.occupant.dna.Clone() if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.dna.real_name + var/mob/living/carbon/human/H = connected.occupant + databuf.dna.real_name = H.dna.real_name + databuf.gender = H.gender + databuf.body_descriptors = H.descriptors databuf.name = "Unique Identifier" src.buffers[bufferId] = databuf return 1 @@ -717,7 +722,10 @@ databuf.types = DNA2_BUF_UI|DNA2_BUF_UE databuf.dna = src.connected.occupant.dna.Clone() if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.dna.real_name + var/mob/living/carbon/human/H = connected.occupant + databuf.dna.real_name = H.dna.real_name + databuf.gender = H.gender + databuf.body_descriptors = H.descriptors databuf.name = "Unique Identifier + Unique Enzymes" src.buffers[bufferId] = databuf return 1 @@ -728,7 +736,10 @@ databuf.types = DNA2_BUF_SE databuf.dna = src.connected.occupant.dna.Clone() if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.dna.real_name + var/mob/living/carbon/human/H = connected.occupant + databuf.dna.real_name = H.dna.real_name + databuf.gender = H.gender + databuf.body_descriptors = H.descriptors databuf.name = "Structural Enzymes" src.buffers[bufferId] = databuf return 1 @@ -764,10 +775,18 @@ if ((buf.types & DNA2_BUF_UE)) src.connected.occupant.real_name = buf.dna.real_name src.connected.occupant.name = buf.dna.real_name + if(ishuman(connected.occupant)) + var/mob/living/carbon/human/H = connected.occupant + H.gender = buf.gender + H.descriptors = buf.body_descriptors src.connected.occupant.UpdateAppearance(buf.dna.UI.Copy()) else if (buf.types & DNA2_BUF_SE) src.connected.occupant.dna.SE = buf.dna.SE src.connected.occupant.dna.UpdateSE() + if(ishuman(connected.occupant)) + var/mob/living/carbon/human/H = connected.occupant + H.gender = buf.gender + H.descriptors = buf.body_descriptors domutcheck(src.connected.occupant,src.connected) src.connected.occupant.apply_effect(rand(20,50), IRRADIATE, check_protection = 0) return 1 diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm index ed27a49ba0..947f06cee4 100644 --- a/code/game/gamemodes/changeling/powers/armblade.dm +++ b/code/game/gamemodes/changeling/powers/armblade.dm @@ -32,7 +32,7 @@ genomecost = 1 verbpath = /mob/proc/changeling_claw -//Grows a scary, and powerful arm blade. +//Grows a scary, and powerful claw. /mob/proc/changeling_claw() set category = "Changeling" set name = "Claw (15)" @@ -62,6 +62,9 @@ var/weapType = "weapon" var/weapLocation = "arm" + defend_chance = 40 // The base chance for the weapon to parry. + projectile_parry_chance = 15 // The base chance for a projectile to be deflected. + /obj/item/weapon/melee/changeling/New(location) ..() START_PROCESSING(SSobj, src) @@ -107,6 +110,28 @@ if(src) qdel(src) +/obj/item/weapon/melee/changeling/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(user.loc, 'sound/weapons/slash.ogg', 50, 1) + return 1 + if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + playsound(user.loc, 'sound/weapons/slash.ogg', 50, 1) + return 1 + + return 0 + +/obj/item/weapon/melee/changeling/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + /obj/item/weapon/melee/changeling/arm_blade name = "arm blade" desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter." @@ -117,11 +142,15 @@ edge = 1 pry = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + defend_chance = 60 + projectile_parry_chance = 25 /obj/item/weapon/melee/changeling/arm_blade/greater name = "arm greatblade" desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter." armor_penetration = 30 + defend_chance = 70 + projectile_parry_chance = 35 /obj/item/weapon/melee/changeling/claw name = "hand claw" @@ -131,9 +160,13 @@ sharp = 1 edge = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + defend_chance = 50 + projectile_parry_chance = 15 /obj/item/weapon/melee/changeling/claw/greater name = "hand greatclaw" force = 20 armor_penetration = 20 - pry = 1 \ No newline at end of file + pry = 1 + defend_chance = 60 + projectile_parry_chance = 25 diff --git a/code/game/gamemodes/changeling/powers/respec.dm b/code/game/gamemodes/changeling/powers/respec.dm index 66f13e720f..1387e14999 100644 --- a/code/game/gamemodes/changeling/powers/respec.dm +++ b/code/game/gamemodes/changeling/powers/respec.dm @@ -7,7 +7,7 @@ if(!changeling) return if(src.mind.changeling.readapts <= 0) - to_chat(src, "We must first absorb another compatable creature!") + to_chat(src, "We must first absorb another compatible creature!") src.mind.changeling.readapts = 0 return diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 7118aafe80..8560582ee8 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -2,6 +2,7 @@ name = "cult blade" desc = "An arcane weapon wielded by the followers of Nar-Sie." icon_state = "cultblade" + origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1) w_class = ITEMSIZE_LARGE force = 30 throwforce = 10 @@ -49,6 +50,7 @@ name = "cult hood" icon_state = "culthood" desc = "A hood worn by the followers of Nar-Sie." + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) flags_inv = HIDEFACE body_parts_covered = HEAD armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) @@ -73,6 +75,7 @@ name = "cult robes" desc = "A set of armored robes worn by the followers of Nar-Sie." icon_state = "cultrobes" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade) armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) @@ -96,6 +99,7 @@ name = "cult helmet" desc = "A space worthy helmet used by the followers of Nar-Sie." icon_state = "cult_helmet" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) siemens_coefficient = 0 @@ -105,6 +109,7 @@ /obj/item/clothing/suit/space/cult name = "cult armour" icon_state = "cult_armour" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy." w_class = ITEMSIZE_NORMAL allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/emergency/oxygen,/obj/item/device/suit_cooling_unit) diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 6de539b878..986800ef9f 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -35,6 +35,22 @@ /obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) attackpylon(user, W.force) +/obj/structure/cult/pylon/take_damage(var/damage) + pylonhit(damage) + +/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) + pylonhit(Proj.get_structure_damage()) + +/obj/structure/cult/pylon/proc/pylonhit(var/damage) + if(!isbroken) + if(prob(1+ damage * 5)) + visible_message("The pylon shatters!") + playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1) + isbroken = 1 + density = 0 + icon_state = "pylon-broken" + set_light(0) + /obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage) if(!isbroken) if(prob(1+ damage * 5)) diff --git a/code/game/gamemodes/cult/soulstone.dm b/code/game/gamemodes/cult/soulstone.dm index 61db88917a..9980f94d12 100644 --- a/code/game/gamemodes/cult/soulstone.dm +++ b/code/game/gamemodes/cult/soulstone.dm @@ -10,7 +10,7 @@ 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 = ITEMSIZE_SMALL slot_flags = SLOT_BELT - origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) + origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4, TECH_ARCANE = 1) var/imprinted = "empty" var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester") diff --git a/code/game/gamemodes/epidemic/epidemic.dm b/code/game/gamemodes/epidemic/epidemic.dm index b6e4146a9c..e6b4f9d456 100644 --- a/code/game/gamemodes/epidemic/epidemic.dm +++ b/code/game/gamemodes/epidemic/epidemic.dm @@ -46,14 +46,8 @@ intercepttext += "* A cure is to be researched immediately, but NanoTrasen intellectual property must be respected. To prevent knowledge of [virus_name] from falling into unauthorized hands, all medical staff that work with the pathogen must be enhanced with a NanoTrasen loyality implant.
    " - for (var/obj/machinery/computer/communications/comm in world) - if (!(comm.stat & (BROKEN | NOPOWER)) && comm.prints_intercept) - var/obj/item/weapon/paper/intercept = new /obj/item/weapon/paper( comm.loc ) - intercept.name = "paper" - intercept.info = intercepttext - - comm.messagetitle.Add("CentCom CONFIDENTIAL REPORT") - comm.messagetext.Add(intercepttext) + //New message handling won't hurt if someone enables epidemic + post_comm_message("Cent. Com. CONFIDENTIAL REPORT", intercepttext) world << sound('sound/AI/commandreport.ogg') @@ -68,14 +62,7 @@ intercepttext += "PATHOGEN [virus_name] IS STILL PRESENT ON [station_name()]. IN COMPLIANCE WITH NANOTRASEN LAWS FOR INTERSTELLAR SAFETY, EMERGENCY SAFETY MEASURES HAVE BEEN AUTHORIZED. ALL INFECTED CREW MEMBERS ON [station_name()] ARE TO BE NEUTRALIZED AND DISPOSED OF IN A MANNER THAT WILL DESTROY ALL TRACES OF THE PATHOGEN. FAILURE TO COMPLY WILL RESULT IN IMMEDIATE DESTRUCTION OF [station_name].
    " intercepttext += "CRUISER WILL ARRIVE IN [round(cruiser_seconds()/60)] MINUTES
    " - for (var/obj/machinery/computer/communications/comm in world) - if (!(comm.stat & (BROKEN | NOPOWER)) && comm.prints_intercept) - var/obj/item/weapon/paper/intercept = new /obj/item/weapon/paper( comm.loc ) - intercept.name = "paper" - intercept.info = intercepttext - - comm.messagetitle.Add("CentCom CONFIDENTIAL REPORT") - comm.messagetext.Add(intercepttext) + post_comm_message("Cent. Com. CONFIDENTIAL REPORT", intercepttext) world << sound('sound/AI/commandreport.ogg') diff --git a/code/game/gamemodes/events/wormholes.dm b/code/game/gamemodes/events/wormholes.dm index cece3f67f9..85b8414813 100644 --- a/code/game/gamemodes/events/wormholes.dm +++ b/code/game/gamemodes/events/wormholes.dm @@ -1,26 +1,33 @@ -/proc/wormhole_event() +/proc/wormhole_event(var/set_duration = 5 MINUTES, var/wormhole_duration_modifier = 1) spawn() var/list/pick_turfs = list() + var/list/Z_choices = list() + Z_choices |= using_map.get_map_levels(1, FALSE) for(var/turf/simulated/floor/T in turfs) - if(T.z in using_map.station_levels) - pick_turfs += T + if(T.z in Z_choices) + if(!T.block_tele) + pick_turfs += T if(pick_turfs.len) + + var/wormhole_max_duration = round((5 MINUTES) * wormhole_duration_modifier) + var/wormhole_min_duration = round((30 SECONDS) * wormhole_duration_modifier) + //All ready. Announce that bad juju is afoot. command_announcement.Announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", new_sound = 'sound/AI/spanomalies.ogg') //prob(20) can be approximated to 1 wormhole every 5 turfs! //admittedly less random but totally worth it >_< - var/event_duration = 3000 //~5 minutes in ticks - var/number_of_selections = (pick_turfs.len/5)+1 //+1 to avoid division by zero! - var/sleep_duration = round( event_duration / number_of_selections ) + var/event_duration = set_duration + var/number_of_selections = round(pick_turfs.len/(4 * (Z_choices.len + 1)))+1 //+1 to avoid division by zero! + var/sleep_duration = 0.2 SECONDS var/end_time = world.time + event_duration //the time by which the event should have ended var/increment = max(1,round(number_of_selections/50)) // world << "DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]" - var/i = 1 - while( 1 ) + var/index = 1 + for(var/I = 1 to number_of_selections) //we've run into overtime. End the event if( end_time < world.time ) @@ -31,26 +38,27 @@ return //loop it round - i += increment - i %= pick_turfs.len - i++ + index += increment + index %= pick_turfs.len + index++ //get our enter and exit locations - var/turf/simulated/floor/enter = pick_turfs[i] + var/turf/simulated/floor/enter = pick_turfs[index] pick_turfs -= enter //remove it from pickable turfs list if( !enter || !istype(enter) ) continue //sanity var/turf/simulated/floor/exit = pick(pick_turfs) - pick_turfs -= exit +// pick_turfs -= exit if( !exit || !istype(exit) ) continue //sanity - create_wormhole(enter,exit) + create_wormhole(enter,exit,wormhole_min_duration,wormhole_max_duration) sleep(sleep_duration) //have a well deserved nap! //maybe this proc can even be used as an admin tool for teleporting players without ruining immulsions? -/proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf) +/proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf, var/min_duration = 30 SECONDS, var/max_duration = 60 SECONDS) + set waitfor = FALSE var/obj/effect/portal/P = new /obj/effect/portal( enter ) P.target = exit P.creator = null @@ -58,5 +66,5 @@ P.failchance = 0 P.icon_state = "anom" P.name = "wormhole" - spawn(rand(300,600)) - qdel(P) \ No newline at end of file + spawn(rand(min_duration,max_duration)) + qdel(P) diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index f0aa03253f..04d8e85363 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -126,7 +126,7 @@ var/global/datum/controller/gameticker/ticker create_characters() //Create player characters and transfer them. collect_minds() equip_characters() - data_core.manifest() + //data_core.manifest() //VOREStation Removal callHook("roundstart") @@ -280,8 +280,13 @@ var/global/datum/controller/gameticker/ticker else if(!player.mind.assigned_role) continue else - if (player.create_character()) // VOREStation Edit + //VOREStation Edit Start + var/mob/living/carbon/human/new_char = player.create_character() + if(new_char) qdel(player) + if(istype(new_char) && !(new_char.mind.assigned_role=="Cyborg")) + data_core.manifest_inject(new_char) + //VOREStation Edit End proc/collect_minds() @@ -299,7 +304,7 @@ var/global/datum/controller/gameticker/ticker if(!player_is_antag(player.mind, only_offstation_roles = 1)) job_master.EquipRank(player, player.mind.assigned_role, 0) UpdateFactionList(player) - equip_custom_items(player) + //equip_custom_items(player) //VOREStation Removal //player.apply_traits() //VOREStation Removal if(captainless) for(var/mob/M in player_list) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 4380331b50..1ac4779414 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -273,7 +273,7 @@ if(explode) explosion(src.loc, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 4, flash_range = 6, adminlog = 0) new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - radiation_repository.radiate(src, 50) + SSradiation.radiate(src, 50) // This meteor fries toasters. /obj/effect/meteor/emp diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 519a1bd5a2..4ee64999fa 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -178,6 +178,10 @@ /proc/get_centcom_access_desc(A) return get_access_desc(A) +/proc/get_access_by_id(id) + var/list/AS = get_all_access_datums_by_id() + return AS[id] + /proc/get_all_jobs() var/list/all_jobs = list() var/list/all_datums = typesof(/datum/job) diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index b63662cd86..084b54cb5c 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -256,7 +256,12 @@ desc = "Quartermaster" region = ACCESS_REGION_SUPPLY -// /var/const/free_access_id = 43 +/var/const/access_network = 42 +/datum/access/network + id = access_network + desc = "Station Network" + region = ACCESS_REGION_RESEARCH + // /var/const/free_access_id = 43 // /var/const/free_access_id = 44 diff --git a/code/game/jobs/job/assistant.dm b/code/game/jobs/job/assistant.dm index 5d7b771398..d20e392599 100644 --- a/code/game/jobs/job/assistant.dm +++ b/code/game/jobs/job/assistant.dm @@ -1,53 +1,26 @@ -//VOREStation Edit - Basically this whole file -/datum/job/intern - title = "Intern" - flag = INTERN - department = "Civilian" - department_flag = ENGSEC // VOREStation Edit - Ran out of bits - faction = "Station" - total_positions = -1 - spawn_positions = -1 - supervisors = "the staff from the departmen you're interning in" - selection_color = "#555555" - economic_modifier = 2 - access = list() //See /datum/job/assistant/get_access() - minimal_access = list() //See /datum/job/assistant/get_access() - outfit_type = /decl/hierarchy/outfit/job/assistant/intern - alt_titles = list("Apprentice Engineer","Medical Intern","Lab Assistant","Security Cadet","Jr. Cargo Tech") //VOREStation Edit - timeoff_factor = 0 //VOREStation Edit - Interns, noh - -//VOREStation Add -/datum/job/intern/New() - ..() - if(config) - total_positions = config.limit_interns - spawn_positions = config.limit_interns -//VOREStation Add End - -// VOREStation Add /datum/job/assistant - title = USELESS_JOB + title = "Assistant" flag = ASSISTANT department = "Civilian" department_flag = CIVILIAN faction = "Station" total_positions = -1 spawn_positions = -1 - supervisors = "nobody! You don't work here" + supervisors = "absolutely everyone" selection_color = "#515151" economic_modifier = 1 - access = list() - minimal_access = list() + access = list() //See /datum/job/assistant/get_access() + minimal_access = list() //See /datum/job/assistant/get_access() outfit_type = /decl/hierarchy/outfit/job/assistant - timeoff_factor = 0 -/datum/job/assistant/New() - ..() - if(config) - total_positions = config.limit_visitors - spawn_positions = config.limit_visitors +/* alt_titles = list( + "Technical Assistant", + "Medical Intern", + "Research Assistant", + "Visitor" = /decl/hierarchy/outfit/job/assistant/visitor + ) */ //VOREStation Removal: no alt-titles for visitors + /datum/job/assistant/get_access() if(config.assistant_maint) return list(access_maint_tunnels) else return list() -//VOREStation Add End diff --git a/code/game/jobs/job/assistant_vr.dm b/code/game/jobs/job/assistant_vr.dm new file mode 100644 index 0000000000..212ca5f847 --- /dev/null +++ b/code/game/jobs/job/assistant_vr.dm @@ -0,0 +1,42 @@ +/datum/job/intern + title = "Intern" + flag = INTERN + department = "Civilian" + department_flag = ENGSEC // Ran out of bits + faction = "Station" + total_positions = -1 + spawn_positions = -1 + supervisors = "the staff from the department you're interning in" + selection_color = "#555555" + economic_modifier = 2 + access = list() //See /datum/job/intern/get_access() + minimal_access = list() //See /datum/job/intern/get_access() + outfit_type = /decl/hierarchy/outfit/job/assistant/intern + alt_titles = list("Apprentice Engineer","Medical Intern","Lab Assistant","Security Cadet","Jr. Cargo Tech", "Jr. Explorer", "Server" = /decl/hierarchy/outfit/job/service/server) + timeoff_factor = 0 // Interns, noh + +/datum/job/intern/New() + ..() + if(config) + total_positions = config.limit_interns + spawn_positions = config.limit_interns + +/datum/job/intern/get_access() + if(config.assistant_maint) + return list(access_maint_tunnels) + else + return list() + +/datum/job/assistant // Visitor + title = USELESS_JOB + supervisors = "nobody! You don't work here" + timeoff_factor = 0 + +/datum/job/assistant/New() + ..() + if(config) + total_positions = config.limit_visitors + spawn_positions = config.limit_visitors + +/datum/job/assistant/get_access() + return list() diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm index 94a32c3841..f1e0a1b8b2 100644 --- a/code/game/jobs/job/captain.dm +++ b/code/game/jobs/job/captain.dm @@ -31,7 +31,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) H.implant_loyalty(src) */ /datum/job/captain/get_access() - return get_all_station_access() + return get_all_station_access().Copy() /datum/job/hop title = "Head of Personnel" @@ -60,13 +60,13 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_hop, access_RC_announce, access_keycard_auth) //, access_gateway) //VOREStation Edit + access_hop, access_RC_announce, access_keycard_auth, access_gateway) minimal_access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_hop, access_RC_announce, access_keycard_auth) //, access_gateway) //VOREStation Edit + access_hop, access_RC_announce, access_keycard_auth, access_gateway) /datum/job/secretary title = "Command Secretary" diff --git a/code/game/jobs/job/captain_vr.dm b/code/game/jobs/job/captain_vr.dm new file mode 100644 index 0000000000..b6c38cae15 --- /dev/null +++ b/code/game/jobs/job/captain_vr.dm @@ -0,0 +1,23 @@ +/datum/job/captain + disallow_jobhop = TRUE + +/datum/job/hop + + disallow_jobhop = TRUE + alt_titles = list("Deputy Director", "Crew Resources Officer") + + access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, + access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, + access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, + access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, + access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, + access_hop, access_RC_announce, access_keycard_auth) + minimal_access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, + access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, + access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, + access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, + access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, + access_hop, access_RC_announce, access_keycard_auth) + +/datum/job/secretary + disallow_jobhop = TRUE \ No newline at end of file diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index 1b59d5a2a0..d31a551726 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -132,8 +132,8 @@ department = "Civilian" department_flag = CIVILIAN faction = "Station" - total_positions = 2 // VOREStation Edit. Original number is 1. - spawn_positions = 2 // VOREStation Edit. Original number is 1. + total_positions = 1 + spawn_positions = 1 supervisors = "the head of personnel" selection_color = "#515151" idtype = /obj/item/weapon/card/id/civilian/librarian diff --git a/code/game/jobs/job/civilian_vr.dm b/code/game/jobs/job/civilian_vr.dm new file mode 100644 index 0000000000..9a13c8925f --- /dev/null +++ b/code/game/jobs/job/civilian_vr.dm @@ -0,0 +1,28 @@ +/datum/job/chef + total_positions = 2 //IT TAKES A LOT TO MAKE A STEW + spawn_positions = 2 //A PINCH OF SALT AND LAUGHTER, TOO + +/datum/job/cargo_tech + total_positions = 3 + spawn_positions = 3 + +/datum/job/mining + total_positions = 4 + spawn_positions = 4 + +/datum/job/janitor //Lots of janitor substations on station. + total_positions = 3 + spawn_positions = 3 + alt_titles = list("Custodian", "Sanitation Technician", "Maid") + +//TFF 5/9/19 - restore librarian job slot to 2 +/datum/job/librarian + total_positions = 2 + spawn_positions = 2 + alt_titles = list("Journalist", "Historian", "Writer") + +/datum/job/lawyer + disallow_jobhop = TRUE + + + diff --git a/code/game/jobs/job/engineering_vr.dm b/code/game/jobs/job/engineering_vr.dm new file mode 100644 index 0000000000..da4b94de38 --- /dev/null +++ b/code/game/jobs/job/engineering_vr.dm @@ -0,0 +1,5 @@ +/datum/job/chief_engineer + disallow_jobhop = TRUE + +/datum/job/atmos + spawn_positions = 3 \ No newline at end of file diff --git a/code/game/jobs/job/job_vr.dm b/code/game/jobs/job/job_vr.dm index 71cb74a950..060aee6fef 100644 --- a/code/game/jobs/job/job_vr.dm +++ b/code/game/jobs/job/job_vr.dm @@ -8,6 +8,9 @@ //Every hour playing this role gains this much time off. (Can be negative for off duty jobs!) var/timeoff_factor = 3 + //Disallow joining as this job midround from off-duty position via going on-duty + var/disallow_jobhop = FALSE + // Check client-specific availability rules. /datum/job/proc/player_has_enough_pto(client/C) return timeoff_factor >= 0 || (C && LAZYACCESS(C.department_hours, department) > 0) diff --git a/code/game/jobs/job/medical_vr.dm b/code/game/jobs/job/medical_vr.dm new file mode 100644 index 0000000000..bd541fb00a --- /dev/null +++ b/code/game/jobs/job/medical_vr.dm @@ -0,0 +1,5 @@ +/datum/job/cmo + disallow_jobhop = TRUE + +/datum/job/doctor + spawn_positions = 5 \ No newline at end of file diff --git a/code/game/jobs/job/offduty_vr.dm b/code/game/jobs/job/offduty_vr.dm index 420943c562..8e18827c06 100644 --- a/code/game/jobs/job/offduty_vr.dm +++ b/code/game/jobs/job/offduty_vr.dm @@ -3,7 +3,7 @@ // /datum/job/offduty_civilian - title = "Off-Duty Worker" + title = "Off-duty Worker" latejoin_only = TRUE timeoff_factor = -1 total_positions = -1 @@ -13,7 +13,7 @@ selection_color = "#9b633e" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) - outfit_type = /decl/hierarchy/outfit/job/assistant + outfit_type = /decl/hierarchy/outfit/job/assistant/worker /datum/job/offduty_cargo title = "Off-duty Cargo" @@ -26,7 +26,7 @@ selection_color = "#9b633e" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) - outfit_type = /decl/hierarchy/outfit/job/assistant + outfit_type = /decl/hierarchy/outfit/job/assistant/cargo /datum/job/offduty_engineering title = "Off-duty Engineer" @@ -39,7 +39,7 @@ selection_color = "#5B4D20" access = list(access_maint_tunnels, access_external_airlocks, access_construction) minimal_access = list(access_maint_tunnels, access_external_airlocks) - outfit_type = /decl/hierarchy/outfit/job/assistant + outfit_type = /decl/hierarchy/outfit/job/assistant/engineer /datum/job/offduty_medical title = "Off-duty Medic" @@ -52,7 +52,7 @@ selection_color = "#013D3B" access = list(access_maint_tunnels, access_external_airlocks) minimal_access = list(access_maint_tunnels, access_external_airlocks) - outfit_type = /decl/hierarchy/outfit/job/assistant + outfit_type = /decl/hierarchy/outfit/job/assistant/medic /datum/job/offduty_science title = "Off-duty Scientist" @@ -65,7 +65,7 @@ selection_color = "#633D63" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) - outfit_type = /decl/hierarchy/outfit/job/assistant + outfit_type = /decl/hierarchy/outfit/job/assistant/scientist /datum/job/offduty_security title = "Off-duty Officer" @@ -78,4 +78,4 @@ selection_color = "#601C1C" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) - outfit_type = /decl/hierarchy/outfit/job/assistant + outfit_type = /decl/hierarchy/outfit/job/assistant/officer diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index bbf4720dfd..c65f3413b6 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -15,11 +15,11 @@ access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_teleporter, access_sec_doors, access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_eva) //VOREStation Edit + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) minimal_access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_teleporter, access_sec_doors, access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_eva) //VOREStation Edit + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) alt_titles = list("Research Supervisor") minimum_character_age = 25 diff --git a/code/game/jobs/job/science_vr.dm b/code/game/jobs/job/science_vr.dm new file mode 100644 index 0000000000..408101842d --- /dev/null +++ b/code/game/jobs/job/science_vr.dm @@ -0,0 +1,14 @@ +/datum/job/rd + disallow_jobhop = TRUE + + access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, + access_tox_storage, access_teleporter, access_sec_doors, + access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_eva, access_network) + minimal_access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, + access_tox_storage, access_teleporter, access_sec_doors, + access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_eva, access_network) + +/datum/job/scientist + alt_titles = list("Xenoarcheologist", "Anomalist", "Phoron Researcher", "Circuit Designer") \ No newline at end of file diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 0316f824c8..6b753f519c 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -14,12 +14,12 @@ economic_modifier = 10 access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks)//VOREStation Edit + access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks)//VOREStation Edit + access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) minimum_character_age = 25 minimal_player_age = 14 diff --git a/code/game/jobs/job/security_vr.dm b/code/game/jobs/job/security_vr.dm new file mode 100644 index 0000000000..8d53dc4a1d --- /dev/null +++ b/code/game/jobs/job/security_vr.dm @@ -0,0 +1,11 @@ +/datum/job/hos + disallow_jobhop = TRUE + + access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, + access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, + access_research, access_engine, access_mining, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, + access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, + access_research, access_engine, access_mining, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) \ No newline at end of file diff --git a/code/game/jobs/job/silicon_vr.dm b/code/game/jobs/job/silicon_vr.dm new file mode 100644 index 0000000000..44cb17ec72 --- /dev/null +++ b/code/game/jobs/job/silicon_vr.dm @@ -0,0 +1,3 @@ +/datum/job/cyborg + total_positions = 4 //Along with one able to spawn later in the round. + spawn_positions = 3 //Let's have 3 able to spawn in roundstart \ No newline at end of file diff --git a/code/game/jobs/job/special.dm b/code/game/jobs/job/special_vr.dm similarity index 99% rename from code/game/jobs/job/special.dm rename to code/game/jobs/job/special_vr.dm index 7d1ccead19..fbed745240 100644 --- a/code/game/jobs/job/special.dm +++ b/code/game/jobs/job/special_vr.dm @@ -37,8 +37,7 @@ return 1 get_access() - var/access = get_all_accesses() - return access + return get_all_accesses().Copy() /*/datum/job/centcom_visitor //For Pleasure // You mean for admin abuse... -Ace title = "CentCom Visitor" diff --git a/code/game/jobs/job/z_all_jobs_vr.dm b/code/game/jobs/job/z_all_jobs_vr.dm deleted file mode 100644 index c0ac7023f6..0000000000 --- a/code/game/jobs/job/z_all_jobs_vr.dm +++ /dev/null @@ -1,55 +0,0 @@ -//Contains all modified jobs for easy access and editing. - -/datum/job/hop - alt_titles = list("Deputy Director", "Crew Resources Officer") - -/datum/job/doctor - total_positions = 5 - spawn_positions = 5 - -/datum/job/janitor //Lots of janitor substations on station. - total_positions = 3 - spawn_positions = 3 - alt_titles = list("Custodian", "Sanitation Technician", "Maid") - -/datum/job/librarian - alt_titles = list("Journalist", "Historian", "Writer") - -/datum/job/officer - total_positions = 4 - spawn_positions = 4 - -/datum/job/cargo_tech - total_positions = 3 - spawn_positions = 3 - -/datum/job/psychiatrist - total_positions = 1 - spawn_positions = 1 - -/datum/job/mining - total_positions = 4 - spawn_positions = 4 - -/datum/job/cyborg - total_positions = 4 //Along with one able to spawn later in the round. - spawn_positions = 3 //Let's have 3 able to spawn in roundstart - -/datum/job/bartender - total_positions = 2 - spawn_positions = 2 - -/datum/job/chef - total_positions = 2 //IT TAKES A LOT TO MAKE A STEW - spawn_positions = 2 //A PINCH OF SALT AND LAUGHTER, TOO - -/datum/job/engineer - total_positions = 5 - spawn_positions = 5 - -/datum/job/atmos - total_positions = 3 - spawn_positions = 3 - -/datum/job/scientist - alt_titles = list("Xenoarcheologist", "Anomalist", "Phoron Researcher", "Circuit Designer") diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 8792a7ba35..d5322d5f0e 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -342,11 +342,13 @@ var/global/datum/controller/occupations/job_master if(!joined_late) var/obj/S = null + var/list/possible_spawns = list() for(var/obj/effect/landmark/start/sloc in landmarks_list) if(sloc.name != rank) continue if(locate(/mob/living) in sloc.loc) continue - S = sloc - break + possible_spawns.Add(sloc) + if(possible_spawns.len) + S = pick(possible_spawns) if(!S) S = locate("start*[rank]") // use old stype if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) @@ -392,6 +394,7 @@ var/global/datum/controller/occupations/job_master if(G.slot == "implant") var/obj/item/weapon/implant/I = G.spawn_item(H) + I.invisibility = 100 I.implant_loadout(H) continue @@ -508,6 +511,29 @@ var/global/datum/controller/occupations/job_master if(job.req_admin_notify) H << "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp." + // EMAIL GENERATION + // Email addresses will be created under this domain name. Mostly for the looks. + var/domain = "freemail.nt" + var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) + var/complete_login = "[sanitized_name]@[domain]" + + // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. + // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. + if(ntnet_global.does_email_exist(complete_login)) + complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" + + // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. + if(ntnet_global.does_email_exist(complete_login)) + to_chat(H, "You were not assigned an email address.") + H.mind.store_memory("You were not assigned an email address.") + else + var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() + EA.password = GenerateKey() + EA.login = complete_login + to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") + H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") + // END EMAIL GENERATION + //Gives glasses to the vision impaired if(H.disabilities & NEARSIGHTED) var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index cd698067f5..086003a479 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -73,18 +73,15 @@ var/list/medical_positions = list( "Geneticist", "Psychiatrist", "Chemist", - "Field Medic", // VOREStation Edit - Moved SAR from planetary -> medical "Paramedic" ) var/list/science_positions = list( "Research Director", - "Pathfinder", // VOREStation Edit - Added Pathfinder "Scientist", "Geneticist", //Part of both medical and science "Roboticist", - "Explorer", // VOREStation Edit - Moved Explorer from planetary -> science "Xenobiologist" ) @@ -104,7 +101,6 @@ var/list/civilian_positions = list( "Librarian", "Lawyer", "Chaplain", - "Pilot", // VOREStation Edit - Moved Pilot from planetary -> civ USELESS_JOB, //VOREStation Edit - Visitor not Assistant "Intern" //VOREStation Edit - Intern ) @@ -119,9 +115,10 @@ var/list/security_positions = list( var/list/planet_positions = list( - // "Explorer", // VOREStation Edit - Moved Explorer from planetary -> science - // "Pilot", // VOREStation Edit - Moved Pilot from planetary -> civ - // "Search and Rescue" // VOREStation Edit - Moved SAR from planetary -> medical + "Pathfinder", // VOREStation Edit - Added Pathfinder + "Explorer", + "Pilot", + "Field Medic" // VOREStation Edit - Field Medic ) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index f7a454a7de..889e11c4e4 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -80,7 +80,7 @@ if(C == user) user.visible_message("[user] climbs on \the [src].","You climb on \the [src].") else - visible_message("\The [C] has been laid on \the [src] by [user].", 3) + visible_message("\The [C] has been laid on \the [src] by [user].") if(C.client) C.client.perspective = EYE_PERSPECTIVE C.client.eye = src diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index b259fe1ded..b73b8b8415 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -26,11 +26,12 @@ for(dir in list(NORTH, EAST, SOUTH, WEST)) // Loop through every direction sleepernew = locate(/obj/machinery/sleeper, get_step(src, dir)) // Try to find a scanner in that direction if(sleepernew) + // VOREStation Edit Start sleeper = sleepernew sleepernew.console = src - set_dir(get_dir(src, sleepernew)) - return - return + break + // VOREStation Edit End + /obj/machinery/sleep_console/attack_ai(var/mob/user) return attack_hand(user) @@ -45,7 +46,7 @@ to_chat(user, "Sleeper not found!") return - if(sleeper.panel_open) + if(panel_open) to_chat(user, "Close the maintenance panel first.") return @@ -108,6 +109,7 @@ else data["beaker"] = -1 data["filtering"] = S.filtering + data["pump"] = S.pumping var/stasis_level_name = "Error!" for(var/N in S.stasis_choices) @@ -142,6 +144,9 @@ if(href_list["sleeper_filter"]) if(S.filtering != text2num(href_list["sleeper_filter"])) S.toggle_filter() + if(href_list["pump"]) + if(S.pumping != text2num(href_list["pump"])) + S.toggle_pump() if(href_list["chemical"] && href_list["amount"]) if(S.occupant && S.occupant.stat != DEAD) if(href_list["chemical"] in S.available_chemicals) // Your hacks are bad and you should feel bad @@ -166,6 +171,7 @@ var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") var/obj/item/weapon/reagent_containers/glass/beaker = null var/filtering = 0 + var/pumping = 0 var/obj/machinery/sleep_console/console var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) @@ -263,6 +269,13 @@ else toggle_filter() + if(pumping > 0) + if(beaker) + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + for(var/datum/reagent/x in occupant.ingested.reagent_list) + occupant.ingested.trans_to_obj(beaker, 3) + else + toggle_pump() /obj/machinery/sleeper/update_icon() icon_state = "sleeper_[occupant ? "1" : "0"]" @@ -301,14 +314,12 @@ return if(UNCONSCIOUS) to_chat(usr, "You struggle through the haze to hit the eject button. This will take a couple of minutes...") - sleep(2 MINUTES) - if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already - return - go_out() + if(do_after(usr, 2 MINUTES, src)) + go_out() if(CONSCIOUS) go_out() else - if(usr.stat != 0) + if(usr.stat != CONSCIOUS) return go_out() add_fingerprint(usr) @@ -326,6 +337,9 @@ if(filtering) toggle_filter() + if(pumping) + toggle_pump() + if(stat & (BROKEN|NOPOWER)) ..(severity) return @@ -340,6 +354,12 @@ return filtering = !filtering +/obj/machinery/sleeper/proc/toggle_pump() + if(!occupant || !beaker) + pumping = 0 + return + pumping = !pumping + /obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) if(!M) return @@ -370,7 +390,8 @@ update_icon() /obj/machinery/sleeper/proc/go_out() - if(!occupant) + if(!occupant || occupant.loc != src) + occupant = null // JUST IN CASE return if(occupant.client) occupant.client.eye = occupant.client.mob @@ -387,6 +408,7 @@ update_use_power(1) update_icon() toggle_filter() + toggle_pump() /obj/machinery/sleeper/proc/remove_beaker() if(beaker) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index ed23884103..484678c942 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -448,4 +448,8 @@ update_flag ..() src.air_contents.adjust_gas("phoron", MolesForPressure()) src.update_icon() - return 1 \ No newline at end of file + return 1 + +/obj/machinery/portable_atmospherics/canister/take_damage(var/damage) + src.health -= damage + healthcheck() \ No newline at end of file diff --git a/code/game/machinery/atmoalter/clamp.dm b/code/game/machinery/atmoalter/clamp.dm new file mode 100644 index 0000000000..318145c229 --- /dev/null +++ b/code/game/machinery/atmoalter/clamp.dm @@ -0,0 +1,154 @@ +//Good luck. --BlueNexus + +//Static version of the clamp +/obj/machinery/clamp + name = "stasis clamp" + desc = "A magnetic clamp which can halt the flow of gas in a pipe, via a localised stasis field." + description_info = "Click-dragging this to yourself while adjacent will attempt to remove it from the pipe." + icon = 'icons/atmos/clamp.dmi' + icon_state = "pclamp0" + anchored = 1.0 + var/obj/machinery/atmospherics/pipe/simple/target = null + var/open = 1 + + var/datum/pipe_network/network_node1 + var/datum/pipe_network/network_node2 + +/obj/machinery/clamp/New(loc, var/obj/machinery/atmospherics/pipe/simple/to_attach = null) + ..() + if(istype(to_attach)) + target = to_attach + else + target = locate(/obj/machinery/atmospherics/pipe/simple) in loc + if(target) + update_networks() + dir = target.dir + return 1 + +/obj/machinery/clamp/proc/update_networks() + if(!target) + return + else + var/obj/machinery/atmospherics/pipe/node1 = target.node1 + var/obj/machinery/atmospherics/pipe/node2 = target.node2 + if(istype(node1)) + var/datum/pipeline/P1 = node1.parent + network_node1 = P1.network + if(istype(node2)) + var/datum/pipeline/P2 = node2.parent + network_node2 = P2.network + +/obj/machinery/clamp/attack_hand(var/mob/user) + if(!target) + return FALSE + if(!open) + open() + else + close() + to_chat(user, "You turn [open ? "off" : "on"] \the [src]") + return TRUE + +/obj/machinery/clamp/Destroy() + if(!open) + spawn(-1) open() + . = ..() + +/obj/machinery/clamp/proc/open() + if(open || !target) + return 0 + + target.build_network() + + + if(network_node1&&network_node2) + network_node1.merge(network_node2) + network_node2 = network_node1 + + if(network_node1) + network_node1.update = 1 + else if(network_node2) + network_node2.update = 1 + + update_networks() + + open = 1 + icon_state = "pclamp0" + target.in_stasis = 0 + return 1 + +/obj/machinery/clamp/proc/close() + if(!open) + return 0 + + qdel(target.parent) + + if(network_node1) + qdel(network_node1) + if(network_node2) + qdel(network_node2) + + var/obj/machinery/atmospherics/pipe/node1 = null + var/obj/machinery/atmospherics/pipe/node2 = null + + if(target.node1) + target.node1.build_network() + node1 = target.node1 + if(target.node2) + target.node2.build_network() + node2 = target.node2 + if(istype(node1) && node1.parent) + var/datum/pipeline/P1 = node1.parent + P1.build_pipeline(node1) + qdel(P1) + if(istype(node2) && node2.parent) + var/datum/pipeline/P2 = node2.parent + P2.build_pipeline(node2) + qdel(P2) +// P1.build_network() +// P2.build_network() + + open = 0 + icon_state = "pclamp1" + target.in_stasis = 1 + + return 1 + +/obj/machinery/clamp/MouseDrop(obj/over_object as obj) + if(!usr) + return + + if(open && over_object == usr && Adjacent(usr)) + to_chat(usr, "You begin to remove \the [src]...") + if (do_after(usr, 30, src)) + to_chat(usr, "You have removed \the [src].") + var/obj/item/clamp/C = new/obj/item/clamp(src.loc) + C.forceMove(usr.loc) + if(ishuman(usr)) + usr.put_in_hands(C) + qdel(src) + return + else + to_chat(usr, "You can't remove \the [src] while it's active!") + +/obj/item/clamp + name = "stasis clamp" + desc = "A magnetic clamp which can halt the flow of gas in a pipe, via a localised stasis field." + icon = 'icons/atmos/clamp.dmi' + icon_state = "pclamp0" + origin_tech = list(TECH_ENGINEERING = 4, TECH_MAGNET = 4) + +/obj/item/clamp/afterattack(var/atom/A, mob/user as mob, proximity) + if(!proximity) + return + + if (istype(A, /obj/machinery/atmospherics/pipe/simple)) + to_chat(user, "You begin to attach \the [src] to \the [A]...") + var/C = locate(/obj/machinery/clamp) in get_turf(A) + if (do_after(user, 30, src) && !C) + if(!user.unEquip(src)) + return + to_chat(user, "You have attached \the [src] to \the [A].") + new/obj/machinery/clamp(A.loc, A) + qdel(src) + if(C) + to_chat(user, "\The [C] is already attached to the pipe at this location!") diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 8778b4e5f3..085c070f7d 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -149,6 +149,8 @@ var/power_losses var/last_power_draw = 0 var/obj/item/weapon/cell/cell + var/use_cell = TRUE + var/removeable_cell = TRUE /obj/machinery/portable_atmospherics/powered/powered() if(use_power) //using area power @@ -158,7 +160,7 @@ return 0 /obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/cell)) + if(use_cell && istype(I, /obj/item/weapon/cell)) if(cell) to_chat(user, "There is already a power cell installed.") return @@ -173,7 +175,7 @@ power_change() return - if(I.is_screwdriver()) + if(I.is_screwdriver() && removeable_cell) if(!cell) to_chat(user, "There is no power cell installed.") return diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index c04760b4ef..fa71e66022 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -84,16 +84,17 @@ else //Make sure it's buildable and list requires resources. for(var/material in R.resources) - var/sheets = round(stored_material[material]/round(R.resources[material]*mat_efficiency)) + var/coeff = (R.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient + var/sheets = round(stored_material[material]/round(R.resources[material]*coeff)) if(isnull(max_sheets) || max_sheets > sheets) max_sheets = sheets - if(!isnull(stored_material[material]) && stored_material[material] < round(R.resources[material]*mat_efficiency)) + if(!isnull(stored_material[material]) && stored_material[material] < round(R.resources[material]*coeff)) can_make = 0 if(!comma) comma = 1 else material_string += ", " - material_string += "[round(R.resources[material] * mat_efficiency)] [material]" + material_string += "[round(R.resources[material] * coeff)] [material]" material_string += ".
    " //Build list of multipliers for sheets. if(R.is_stack) @@ -252,15 +253,16 @@ update_use_power(2) //Check if we still have the materials. + var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient for(var/material in making.resources) if(!isnull(stored_material[material])) - if(stored_material[material] < round(making.resources[material] * mat_efficiency) * multiplier) + if(stored_material[material] < round(making.resources[material] * coeff) * multiplier) return //Consume materials. for(var/material in making.resources) if(!isnull(stored_material[material])) - stored_material[material] = max(0, stored_material[material] - round(making.resources[material] * mat_efficiency) * multiplier) + stored_material[material] = max(0, stored_material[material] - round(making.resources[material] * coeff) * multiplier) update_icon() // So lid closes @@ -275,9 +277,13 @@ //Create the desired item. var/obj/item/I = new making.path(src.loc) - if(multiplier > 1 && istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - S.amount = multiplier + if(multiplier > 1) + if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + S.amount = multiplier + else + for(multiplier; multiplier > 1; --multiplier) // Create multiple items if it's not a stack. + new making.path(src.loc) updateUsrDialog() diff --git a/code/game/machinery/autolathe_vr.dm b/code/game/machinery/autolathe_vr.dm deleted file mode 100644 index 489c97f2f5..0000000000 --- a/code/game/machinery/autolathe_vr.dm +++ /dev/null @@ -1,17 +0,0 @@ -/datum/category_item/autolathe/arms/classic_smg_9mm - name = "SMG magazine (9mm)" - path = /obj/item/ammo_magazine/m9mml - hidden = 1 -/* De-coded? -/datum/category_item/autolathe/arms/classic_smg_9mmr - name = "SMG magazine (9mm rubber)" - path = /obj/item/ammo_magazine/m9mml/rubber - -/datum/category_item/autolathe/arms/classic_smg_9mmp - name = "SMG magazine (9mm practice)" - path = /obj/item/ammo_magazine/m9mml/practice - -/datum/category_item/autolathe/arms/classic_smg_9mmf - name = "SMG magazine (9mm flash)" - path = /obj/item/ammo_magazine/m9mml/flash -*/ \ No newline at end of file diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index f60fcabbfa..19c790b06b 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -21,6 +21,10 @@ var/loaded_dna //Blood sample for DNA hashing. var/malfunctioning = FALSE // May cause rejection, or the printing of some alien limb instead! + var/complex_organs = FALSE // Can it print more 'complex' organs? + + var/anomalous_organs = FALSE // Can it print anomalous organs? + // These should be subtypes of /obj/item/organ // Costs roughly 20u Phoron (1 sheet) per internal organ, limbs are 60u for limb and extremity var/list/products = list( @@ -29,6 +33,7 @@ "Kidneys" = list(/obj/item/organ/internal/kidneys,20), "Eyes" = list(/obj/item/organ/internal/eyes, 20), "Liver" = list(/obj/item/organ/internal/liver, 20), + "Spleen" = list(/obj/item/organ/internal/spleen, 20), "Arm, Left" = list(/obj/item/organ/external/arm, 40), "Arm, Right" = list(/obj/item/organ/external/arm/right, 40), "Leg, Left" = list(/obj/item/organ/external/leg, 40), @@ -39,6 +44,18 @@ "Hand, Right" = list(/obj/item/organ/external/hand/right, 20) ) + var/list/complex_products = list( + "Brain" = list(/obj/item/organ/internal/brain, 60), + "Larynx" = list(/obj/item/organ/internal/voicebox, 20), + "Head" = list(/obj/item/organ/external/head, 40) + ) + + var/list/anomalous_products = list( + "Lymphatic Complex" = list(/obj/item/organ/internal/immunehub, 120), + "Respiration Nexus" = list(/obj/item/organ/internal/lungs/replicant/mending, 80), + "Adrenal Valve Cluster" = list(/obj/item/organ/internal/heart/replicant/rage, 80) + ) + /obj/machinery/organ_printer/attackby(var/obj/item/O, var/mob/user) if(default_deconstruction_screwdriver(user, O)) updateUsrDialog() @@ -90,6 +107,17 @@ else malfunctioning = initial(malfunctioning) + if(manip_rating >= 3) + complex_organs = TRUE + if(manip_rating >= 4) + anomalous_organs = TRUE + if(manip_rating >= 5) + malfunctioning = TRUE + else + complex_organs = initial(complex_organs) + anomalous_organs = initial(anomalous_organs) + malfunctioning = initial(malfunctioning) + . = ..() /obj/machinery/organ_printer/attack_hand(mob/user) @@ -113,7 +141,17 @@ to_chat(user, "\The [src] can't operate without a reagent reservoir!") /obj/machinery/organ_printer/proc/printing_menu(mob/user) - var/choice = input("What would you like to print?") as null|anything in products + var/list/possible_list = list() + + possible_list |= products + + if(complex_organs) + possible_list |= complex_products + + if(anomalous_organs) + possible_list |= anomalous_products + + var/choice = input("What would you like to print?") as null|anything in possible_list if(!choice || printing || (stat & (BROKEN|NOPOWER))) return diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index e8b845ca78..34fda2bb06 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -40,6 +40,24 @@ var/list/camera_computers_using_this = list() +/obj/machinery/camera/apply_visual(mob/living/carbon/human/M) + if(!M.client) + return + M.overlay_fullscreen("fishbed",/obj/screen/fullscreen/fishbed) + M.overlay_fullscreen("scanlines",/obj/screen/fullscreen/scanline) + M.overlay_fullscreen("whitenoise",/obj/screen/fullscreen/noise) + M.machine_visual = src + return 1 + +/obj/machinery/camera/remove_visual(mob/living/carbon/human/M) + if(!M.client) + return + M.clear_fullscreen("fishbed",0) + M.clear_fullscreen("scanlines") + M.clear_fullscreen("whitenoise") + M.machine_visual = null + return 1 + /obj/machinery/camera/New() wires = new(src) assembly = new(src) @@ -93,10 +111,9 @@ stat |= EMPED set_light(0) triggerCameraAlarm() - kick_viewers() update_icon() update_coverage() - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) /obj/machinery/camera/bullet_act(var/obj/item/projectile/P) take_damage(P.get_structure_damage()) @@ -246,28 +263,27 @@ user = null if(choice != 1) - //legacy support, if choice is != 1 then just kick viewers without changing status - kick_viewers() - else - set_status(!src.status) - if (!(src.status)) - if(user) - visible_message(" [user] has deactivated [src]!") - else - visible_message(" [src] clicks and shuts down. ") - playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = "[initial(icon_state)]1" - add_hiddenprint(user) - else - if(user) - visible_message(" [user] has reactivated [src]!") - else - visible_message(" [src] clicks and reactivates itself. ") - playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = initial(icon_state) - add_hiddenprint(user) + return -/obj/machinery/camera/proc/take_damage(var/force, var/message) + set_status(!src.status) + if (!(src.status)) + if(user) + visible_message(" [user] has deactivated [src]!") + else + visible_message(" [src] clicks and shuts down. ") + playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = "[initial(icon_state)]1" + add_hiddenprint(user) + else + if(user) + visible_message(" [user] has reactivated [src]!") + else + visible_message(" [src] clicks and reactivates itself. ") + playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = initial(icon_state) + add_hiddenprint(user) + +/obj/machinery/camera/take_damage(var/force, var/message) //prob(25) gives an average of 3-4 hits if (force >= toughness && (force > toughness*4 || prob(25))) destroy() @@ -277,7 +293,6 @@ stat |= BROKEN wires.RandomCutAll() - kick_viewers() triggerCameraAlarm() update_icon() update_coverage() @@ -292,26 +307,12 @@ if (status != newstatus) status = newstatus update_coverage() - // 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 couldn't use it anyway? - kick_viewers() /obj/machinery/camera/check_eye(mob/user) if(!can_use()) return -1 if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS return 0 -//This might be redundant, because of check_eye() -/obj/machinery/camera/proc/kick_viewers() - for(var/mob/O in player_list) - if (istype(O.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/S = O.machine - if (S.current_camera == src) - O.unset_machine() - O.reset_view(null) - O << "The screen bursts into static." - /obj/machinery/camera/update_icon() if (!status || (stat & BROKEN)) icon_state = "[initial(icon_state)]1" diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index d4b29b217e..206dffaa43 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -25,10 +25,10 @@ var/global/list/station_networks = list( var/global/list/engineering_networks = list( NETWORK_ENGINE, NETWORK_ENGINEERING, - NETWORK_ENGINEERING_OUTPOST, - "Atmosphere Alarms", - "Fire Alarms", - "Power Alarms") + //NETWORK_ENGINEERING_OUTPOST, //VOREStation Edit: Tether has no Engineering Outpost, + NETWORK_ALARM_ATMOS, + NETWORK_ALARM_FIRE, + NETWORK_ALARM_POWER) /obj/machinery/camera/network/crescent network = list(NETWORK_CRESCENT) @@ -43,6 +43,9 @@ var/global/list/engineering_networks = list( /obj/machinery/camera/network/civilian network = list(NETWORK_CIVILIAN) +/obj/machinery/camera/network/circuits + network = list(NETWORK_CIRCUITS) + /* /obj/machinery/camera/network/civilian_east network = list(NETWORK_CIVILIAN_EAST) diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index bad819a985..7c7f704392 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -7,9 +7,19 @@ use_power = 1 idle_power_usage = 5 active_power_usage = 60000 //60 kW. (this the power drawn when charging) + var/efficiency = 60000 //will provide the modified power rate when upgraded power_channel = EQUIP var/obj/item/weapon/cell/charging = null var/chargelevel = -1 + circuit = /obj/item/weapon/circuitboard/cell_charger + +/obj/machinery/cell_charger/New() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/capacitor(src) + component_parts += new /obj/item/stack/cable_coil(src, 5) + RefreshParts() + ..() + return /obj/machinery/cell_charger/update_icon() icon_state = "ccharger[charging ? 1 : 0]" @@ -32,9 +42,9 @@ if(!..(user, 5)) return - user << "There's [charging ? "a" : "no"] cell in the charger." + to_chat(user, "[charging ? "[charging]" : "Nothing"] is in [src].") if(charging) - user << "Current charge: [charging.charge]" + to_chat(user, "Current charge: [charging.charge] / [charging.maxcharge]") /obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user) if(stat & BROKEN) @@ -42,56 +52,60 @@ if(istype(W, /obj/item/weapon/cell) && anchored) if(istype(W, /obj/item/weapon/cell/device)) - user << " The charger isn't fitted for that type of cell." + to_chat(user, "\The [src] isn't fitted for that type of cell.") return if(charging) - user << "There is already a cell in the charger." + to_chat(user, "There is already [charging] in [src].") return else var/area/a = loc.loc // Gets our locations location, like a dream within a dream if(!isarea(a)) return if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - user << "The [name] blinks red as you try to insert the cell!" + to_chat(user, "\The [src] blinks red as you try to insert [W]!") return user.drop_item() W.loc = src charging = W - user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") + user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") chargelevel = -1 update_icon() else if(W.is_wrench()) if(charging) - user << "Remove the cell first!" + to_chat(user, "Remove [charging] first!") return anchored = !anchored - user << "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground" + to_chat(user, "You [anchored ? "attach" : "detach"] [src] [anchored ? "to" : "from"] the ground") playsound(src, W.usesound, 75, 1) + else if(default_deconstruction_screwdriver(user, W)) + return + else if(default_deconstruction_crowbar(user, W)) + return + else if(default_part_replacement(user, W)) + return /obj/machinery/cell_charger/attack_hand(mob/user) + add_fingerprint(user) + if(charging) - usr.put_in_hands(charging) - charging.add_fingerprint(user) + user.put_in_hands(charging) charging.update_icon() + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") charging = null - user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") chargelevel = -1 update_icon() /obj/machinery/cell_charger/attack_ai(mob/user) if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Borgs can remove the cell if they are near enough - if(!charging) - return - - charging.loc = src.loc - charging.update_icon() - charging = null - update_icon() - user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") - + if(charging) + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + charging.loc = src.loc + charging.update_icon() + charging = null + update_icon() /obj/machinery/cell_charger/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -108,9 +122,15 @@ return if(charging && !charging.fully_charged()) - charging.give(active_power_usage*CELLRATE) + charging.give(efficiency*CELLRATE) update_use_power(2) update_icon() else update_use_power(1) + +/obj/machinery/cell_charger/RefreshParts() + var/E = 0 + for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) + E += C.rating + efficiency = active_power_usage * (1+ (E - 1)*0.5) \ No newline at end of file diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 8e3cb117f9..52a31d5afe 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -113,6 +113,8 @@ if(!R.dna.real_name) //to prevent null names R.dna.real_name = "clone ([rand(0,999)])" H.real_name = R.dna.real_name + H.gender = R.gender + H.descriptors = R.body_descriptors //Get the clone body ready H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 4ac34cf157..c4930193b5 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -248,7 +248,7 @@ icon_state = "television" icon_keyboard = null icon_screen = "detective_tv" - circuit = null + circuit = /obj/item/weapon/circuitboard/security/tv light_color = "#3848B3" light_power_on = 0.5 diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 42a418c7ad..eb78d41e26 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -108,7 +108,7 @@ list("cat" = "Science", "jobs" = format_jobs(science_positions)), list("cat" = "Security", "jobs" = format_jobs(security_positions)), list("cat" = "Cargo", "jobs" = format_jobs(cargo_positions)), - list("cat" = "Planetside", "jobs" = format_jobs(planet_positions)), + list("cat" = "Exploration", "jobs" = format_jobs(planet_positions)), //VOREStation Edit list("cat" = "Civilian", "jobs" = format_jobs(civilian_positions)), list("cat" = "CentCom", "jobs" = format_jobs(get_all_centcom_jobs())) ) @@ -279,7 +279,7 @@ if ("terminate") if (is_authenticated()) - modify.assignment = "Terminated" + modify.assignment = "Dismissed" //VOREStation Edit: setting adjustment modify.access = list() callHook("terminate_employee", list(modify)) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 96e6f6b5f9..5178d77d82 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -335,6 +335,8 @@ R.name = R.dna.real_name R.types = DNA2_BUF_UI|DNA2_BUF_UE|DNA2_BUF_SE R.languages = subject.languages + R.gender = subject.gender + R.body_descriptors = subject.descriptors if(!brain_skip) //Brains don't have flavor text. R.flavor = subject.flavor_texts.Copy() else diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 5aca29433b..8cd15a260c 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -531,6 +531,13 @@ message_admins("[key_name_admin(user)] has recalled the shuttle.", 1) return + +/proc/is_relay_online() + for(var/obj/machinery/telecomms/relay/M in world) + if(M.stat == 0) + return 1 + return 0 + /obj/machinery/computer/communications/proc/post_status(var/command, var/data1, var/data2) var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 0c24c15b3e..da86e4ee3c 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -553,4 +553,5 @@ icon_state = "laptop" icon_keyboard = "laptop_key" icon_screen = "medlaptop" - density = 0 \ No newline at end of file + circuit = /obj/item/weapon/circuitboard/med_data/laptop + density = 0 diff --git a/code/game/machinery/computer/shutoff_monitor.dm b/code/game/machinery/computer/shutoff_monitor.dm new file mode 100644 index 0000000000..4dd79376c9 --- /dev/null +++ b/code/game/machinery/computer/shutoff_monitor.dm @@ -0,0 +1,42 @@ +/obj/machinery/computer/shutoff_monitor + name = "automated shutoff valve monitor" + desc = "Console used to remotely monitor shutoff valves on the station." + icon_keyboard = "power_key" + icon_screen = "power:0" + light_color = "#a97faa" + circuit = /obj/item/weapon/circuitboard/shutoff_monitor + +/obj/machinery/computer/shutoff_monitor/attack_hand(var/mob/user) + ..() + ui_interact(user) + +/obj/machinery/computer/shutoff_monitor/attack_robot(var/mob/user) // Borgs and AI will want to see this too + ..() + ui_interact(user) + +/obj/machinery/computer/shutoff_monitor/attack_ai(var/mob/user) + ui_interact(user) + +/obj/machinery/computer/shutoff_monitor/ui_interact(mob/user, ui_key = "shutoff_monitor", var/datum/nanoui/ui = null, var/force_open = 1, var/key_state = null) + var/data[0] + data["valves"] = list() + for(var/obj/machinery/atmospherics/valve/shutoff/S in GLOB.shutoff_valves) + data["valves"][++data["valves"].len] = list("name" = S.name, "enable" = S.close_on_leaks, "open" = S.open, "x" = S.x, "y" = S.y, "z" = S.z) + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "shutoff_monitor.tmpl", "Automated Shutoff Valve Monitor", 625, 700, state = key_state) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every 20 Master Controller tick + ui.set_auto_update(20) // Longer term to reduce the rate of data collection and processing + +/obj/machinery/computer/shutoff_monitor/update_icon() + ..() + if(!(stat & (NOPOWER|BROKEN))) + overlays += image('icons/obj/computer.dmi', "ai-fixer-empty", overlay_layer) diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm index caeecb2e27..4969e89334 100644 --- a/code/game/machinery/computer/timeclock_vr.dm +++ b/code/game/machinery/computer/timeclock_vr.dm @@ -18,6 +18,12 @@ clicksound = null var/obj/item/weapon/card/id/card // Inserted Id card + var/obj/item/device/radio/intercom/announce // Integreated announcer + + +/obj/machinery/computer/timeclock/New() + announce = new /obj/item/device/radio/intercom(src) + ..() /obj/machinery/computer/timeclock/Destroy() if(card) @@ -81,12 +87,12 @@ "department" = job.department, "selection_color" = job.selection_color, "economic_modifier" = job.economic_modifier, - "head_position" = job.head_position, "timeoff_factor" = job.timeoff_factor ) - // TODO - Once job changing is implemented, we will want to list jobs to change into. - // if(job && job.timeoff_factor < 0) // Currently are Off Duty, so gotta lookup what on-duty jobs are open - // data["job_choices"] = getOpenOnDutyJobs(user, job.department) + if(config.time_off && config.pto_job_change) + data["allow_change_job"] = TRUE + if(job && job.timeoff_factor < 0) // Currently are Off Duty, so gotta lookup what on-duty jobs are open + data["job_choices"] = getOpenOnDutyJobs(user, job.department) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -101,7 +107,7 @@ src.add_fingerprint(usr) if (href_list["id"]) - if (card) + if(card) usr.put_in_hands(card) card = null else @@ -110,8 +116,128 @@ I.forceMove(src) card = I update_icon() + return 1 + if(href_list["switch-to-onduty"]) + if(card) + if(checkFace()) + if(checkCardCooldown()) + makeOnDuty(href_list["switch-to-onduty"]) + usr.put_in_hands(card) + card = null + update_icon() + return 1 + if(href_list["switch-to-offduty"]) + if(card) + if(checkFace()) + if(checkCardCooldown()) + makeOffDuty() + usr.put_in_hands(card) + card = null + update_icon() + return 1 return 1 // Return 1 to update UI +/obj/machinery/computer/timeclock/proc/getOpenOnDutyJobs(var/mob/user, var/department) + var/list/available_jobs = list() + for(var/datum/job/job in job_master.occupations) + if(job && job.is_position_available() && !job.whitelist_only && !jobban_isbanned(user,job.title) && job.player_old_enough(user.client)) + if(job.department == department && !job.disallow_jobhop && job.timeoff_factor > 0) + available_jobs += job.title + if(job.alt_titles) + for(var/alt_job in job.alt_titles) + available_jobs += alt_job + return available_jobs + +/obj/machinery/computer/timeclock/proc/makeOnDuty(var/newjob) + var/datum/job/foundjob = null + for(var/datum/job/job in job_master.occupations) + if(newjob == job.title) + foundjob = job + break + if(newjob in job.alt_titles) + foundjob = job + break + if(!newjob in getOpenOnDutyJobs(usr, job_master.GetJob(card.rank).department)) + return + if(foundjob && card) + card.access = foundjob.get_access() + card.rank = foundjob.title + card.assignment = newjob + card.name = text("[card.registered_name]'s ID Card ([card.assignment])") + data_core.manifest_modify(card.registered_name, card.assignment) + card.last_job_switch = world.time + callHook("reassign_employee", list(card)) + foundjob.current_positions++ + var/mob/living/carbon/human/H = usr + H.mind.assigned_role = foundjob.title + H.mind.role_alt_title = newjob + announce.autosay("[card.registered_name] has moved On-Duty as [card.assignment].", "Employee Oversight") + return + +/obj/machinery/computer/timeclock/proc/makeOffDuty() + var/datum/job/foundjob = null + for(var/datum/job/job in job_master.occupations) + if(card.rank == job.title) + foundjob = job + break + if(!foundjob) + return + var/real_dept = foundjob.department + if(real_dept && real_dept == "Command") + real_dept = "Civilian" + var/datum/job/ptojob = null + for(var/datum/job/job in job_master.occupations) + if(job.department == real_dept && job.timeoff_factor < 0) + ptojob = job + break + if(ptojob && card) + var/oldtitle = card.assignment + card.access = ptojob.get_access() + card.rank = ptojob.title + card.assignment = ptojob.title + card.name = text("[card.registered_name]'s ID Card ([card.assignment])") + data_core.manifest_modify(card.registered_name, card.assignment) + card.last_job_switch = world.time + callHook("reassign_employee", list(card)) + var/mob/living/carbon/human/H = usr + H.mind.assigned_role = ptojob.title + H.mind.role_alt_title = ptojob.title + foundjob.current_positions-- + announce.autosay("[card.registered_name], [oldtitle], has moved Off-Duty.", "Employee Oversight") + return + +/obj/machinery/computer/timeclock/proc/checkCardCooldown() + if(!card) + return FALSE + if((world.time - card.last_job_switch) < 15 MINUTES) + to_chat(usr, "You need to wait at least 15 minutes after last duty switch.") + return FALSE + return TRUE + +/obj/machinery/computer/timeclock/proc/checkFace() + if(!card) + to_chat(usr, "No ID is inserted.") + return FALSE + var/mob/living/carbon/human/H = usr + if(!(istype(H))) + to_chat(usr, "Invalid user detected. Access denied.") + return FALSE + else if((H.wear_mask && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE))) //Face hiding bad + to_chat(usr, "Facial recognition scan failed due to physical obstructions. Access denied.") + return FALSE + else if(H.get_face_name() == "Unknown" || !(H.real_name == card.registered_name)) + to_chat(usr, "Facial recognition scan failed. Access denied.") + return FALSE + else + return TRUE + +/obj/item/weapon/card/id + var/last_job_switch + +/obj/item/weapon/card/id/New() + .=..() + last_job_switch = world.time + // // Frame type for construction // @@ -144,4 +270,4 @@ /obj/machinery/computer/timeclock/premade/west dir = 4 - pixel_x = -26 + pixel_x = -26 \ No newline at end of file diff --git a/code/game/machinery/computer3/NTOS.dm b/code/game/machinery/computer3/NTOS.dm deleted file mode 100644 index a9068a634f..0000000000 --- a/code/game/machinery/computer3/NTOS.dm +++ /dev/null @@ -1,233 +0,0 @@ -/* - The Big Bad NT Operating System -*/ - -/datum/file/program/ntos - name = "NanoTrasen Operating System" - extension = "prog" - active_state = "ntos" - var/obj/item/part/computer/storage/current // the drive being viewed, null for desktop/computer - var/fileop = "runfile" - -/* - Generate a basic list of files in the selected scope -*/ - -/datum/file/program/ntos/proc/list_files() - if(!computer || !current) return null - return current.files - - -/datum/file/program/ntos/proc/filegrid(var/list/filelist) - var/dat = "" - var/i = 0 - for(var/datum/file/F in filelist) - if(!F.hidden_file) - i++ - if(i==1) - dat += "" - if(i>= 6) - i = 0 - dat += "" - continue - dat += {" - "} - - dat += "
    -
    -
    - [F.name] -
    -
    " - return dat - -// -// I am separating this from filegrid so that I don't have to -// make metadata peripheral files -// -/datum/file/program/ntos/proc/desktop(var/peripheralop = "viewperipheral") - var/dat = "" - var/i = 0 - var/list/peripherals = list(computer.hdd,computer.floppy,computer.cardslot) - for(var/obj/item/part/computer/C in peripherals) - if(!istype(C)) continue - i++ - if(i==1) - dat += "" - if(i>= 6) - i = 0 - dat += "" - continue - dat += {" - "} - - dat += "
    - - \icon[C]
    - [C.name] -
    -
    " - return dat - - -/datum/file/program/ntos/proc/window(var/title,var/buttonbar,var/content) - return {" -
    -
    [title]
    -
    [buttonbar]
    -
    [content]
    -
    "} - -/datum/file/program/ntos/proc/buttonbar(var/type = 0) - switch(type) - if(0) // FILE OPERATIONS - return {""} - -/datum/file/program/ntos/interact() - if(!interactable()) - return - var/dat = {" - - - Operating System - - - -
    "} - - - dat += generate_status_bar() - var/list/files = list_files() - if(current) - dat +=window(current.name,buttonbar(),filegrid(files)) - else - dat += desktop() - - dat += "
    " - - usr << browse(dat, "window=\ref[computer];size=670x510") - onclose(usr, "\ref[computer]") - - // STATUS BAR - // Small 16x16 icons representing status of components, etc. - // Currently only used by battery icon - // TODO: Add more icons! -/datum/file/program/ntos/proc/generate_status_bar() - var/dat = "" - - // Battery level icon - switch(computer.check_battery_status()) - if(-1) - dat += "" - if(0 to 5) - dat += "" - if(6 to 20) - dat += "" - if(21 to 40) - dat += "" - if(41 to 60) - dat += "" - if(61 to 80) - dat += "" - if(81 to 100) - dat += "" - dat += "
    " - return dat - -/datum/file/program/ntos/Topic(href, list/href_list) - if(!interactable() || ..(href,href_list)) - return - - if("viewperipheral" in href_list) // open drive, show status of peripheral - var/obj/item/part/computer/C = locate(href_list["viewperipheral"]) in src.computer - if(!istype(C)) - return - - if(istype(C,/obj/item/part/computer/storage)) - current = C - interact() - return - // else ??? - if(istype(C,/obj/item/part/computer/cardslot)) - computer.cardslot.remove(usr) - interact() - return - - // distinct from close, this is the file dialog window - if("winclose" in href_list) - current = null - interact() - return - -#undef MAX_ROWS -#undef MAX_COLUMNS diff --git a/code/game/machinery/computer3/bios.dm b/code/game/machinery/computer3/bios.dm deleted file mode 100644 index c5b69e755b..0000000000 --- a/code/game/machinery/computer3/bios.dm +++ /dev/null @@ -1,176 +0,0 @@ -/* - Okay so my last effort to have a central BIOS function was interesting - but completely unmaintainable, I have scrapped it. - - The parts that were actually useful will be put here in functions instead. - If we want a central bios function we can add one that just indexes them. - That should at least allow sensible debugging. -*/ - -/obj/machinery/computer3 - - /* - interactable(user): performs all standard sanity checks - Call in topic() and interact(). - */ -/obj/machinery/computer3/proc/interactable(var/mob/user) - if( !src || !user || stat || user.stat || user.lying || user.blinded ) - return 0 - if(!program) - return 0 - if(!isturf(loc) || !isturf(user.loc)) // todo handheld maybe - return 0 - if(user.restrained()) - to_chat(user, "You need a free hand!") - return 0 - - if(issilicon(user) &&!program.ai_allowed ) - to_chat(user, "You are forbidden from accessing this program.") - return 0 - if(!ishuman(user) && program.human_controls) - to_chat(user, "Your body can't work the controls!") - return 0 - - - if(!in_range(src,user) && (!program.human_controls || !istype(user.get_active_hand(),/obj/item/tk_grab))) - // telekinesis check - to_chat(user, "It's too complicated to work at a distance!") - return 0 - - add_fingerprint(user) - user.set_machine(src) - return 1 - - /* - Deduplicates an item list and gives you range and direction. - This is used for networking so you can determine which of several - identically named objects you're referring to. - */ -/obj/machinery/computer3/proc/format_atomlist(var/list/atoms) - var/list/output = list() - for(var/atom/A in atoms) - var/title = "[A] (Range [get_dist(A,src)] meters, [dir2text(get_dir(src,A))])" - output[title] = A - return output - - /* - This is used by the camera monitoring program to see if you're still in range - */ -/obj/machinery/computer3/check_eye(var/mob/user as mob) - if(!interactable(user) || user.machine != src) - if(user.machine == src) - user.unset_machine() - return -1 - - var/datum/file/program/security/S = program - if( !istype(S) || !S.current || !S.current.status || !camnet ) - if( user.machine == src ) - user.unset_machine() - return -1 - - user.reset_view(S.current, 0) - return 0 - - /* - List all files, including removable disks and data cards - (I don't know why but I don't want to rip data cards out. - It just seems... interesting?) - */ -/obj/machinery/computer3/proc/list_files(var/typekey = null) - var/list/files = list() - if(hdd) - files += hdd.files - if(floppy && floppy.inserted) - files += floppy.inserted.files - if(cardslot && istype(cardslot.reader,/obj/item/weapon/card/data)) - files += cardslot.reader:files - if(!ispath(typekey)) - return files - - var/i = 1 - while(i<=files.len) - if(istype(files[i],typekey)) - i++ - continue - files.Cut(i,i+1) - return files - - /* - Crash the computer with an error. - Todo: redo - */ -/obj/machinery/computer3/proc/Crash(var/errorcode = PROG_CRASH) - if(!src) - return null - - switch(errorcode) - if(PROG_CRASH) - if(usr) - usr << "The program crashed!" - usr << browse(null,"\ref[src]") - Reset() - - if(MISSING_PERIPHERAL) - Reset() - if(usr) - usr << browse("

    ERROR: Missing or disabled component

    A hardware failure has occured. Please insert or replace the missing or damaged component and restart the computer.","window=\ref[src]") - - if(BUSTED_ASS_COMPUTER) - Reset() - os.error = BUSTED_ASS_COMPUTER - if(usr) - usr << browse("

    ERROR: Missing or disabled component

    A hardware failure has occured. Please insert or replace the missing or damaged component and restart the computer.","window=\ref[src]") - - if(MISSING_PROGRAM) - Reset() - if(usr) - usr << browse("

    ERROR: No associated program

    This file requires a specific program to open, which cannot be located. Please install the related program and try again.","window=\ref[src]") - - if(FILE_DRM) - Reset() - if(usr) - usr << browse("

    ERROR: File operation prohibited

    Copy protection exception: missing authorization token.","window=\ref[src]") - - if(NETWORK_FAILURE) - Reset() - if(usr) - usr << browse("

    ERROR: Networking exception: Unable to connect to remote host.

    ","window=\ref[src]") - - else - if(usr) - usr << "The program crashed!" - usr << browse(null,"\ref[src]") - testing("computer/Crash() - unknown error code [errorcode]") - Reset() - return null - - #define ANY_DRIVE 0 - #define PREFER_FLOPPY 1 - #define PREFER_CARD 2 - #define PREFER_HDD 4 - - - // required_location: only put on preferred devices -/obj/machinery/computer3/proc/writefile(var/datum/file/F, var/where = ANY_DRIVE, var/required_location = 0) - if(where != ANY_DRIVE) - if((where&PREFER_FLOPPY) && floppy && floppy.addfile(F)) - return 1 - if((where&PREFER_CARD) && istype(cardslot, /obj/item/part/computer/cardslot/dual)) - var/obj/item/part/computer/cardslot/dual/D = cardslot - if(D.addfile(F)) - return 1 - if((where&PREFER_HDD) && hdd && hdd.addfile(F)) - return 1 - - if(required_location) - return 0 - - if(floppy && floppy.addfile(F)) - return 1 - if(istype(cardslot, /obj/item/part/computer/cardslot/dual)) - var/obj/item/part/computer/cardslot/dual/D = cardslot - if(D.addfile(F)) - return 1 - if(hdd && hdd.addfile(F)) - return 1 - return 0 diff --git a/code/game/machinery/computer3/buildandrepair.dm b/code/game/machinery/computer3/buildandrepair.dm deleted file mode 100644 index 928675769c..0000000000 --- a/code/game/machinery/computer3/buildandrepair.dm +++ /dev/null @@ -1,303 +0,0 @@ -// Computer3 circuitboard specifically -/obj/item/part/computer/circuitboard - density = 0 - anchored = 0 - w_class = ITEMSIZE_SMALL - name = "Circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "id_mod" - item_state = "electronic" - origin_tech = list(TECH_DATA = 2) - var/id = null - var/frequency = null - var/build_path = null - var/board_type = "computer" - var/list/req_components = null - var/powernet = null - var/list/records = null - - var/datum/file/program/OS = new/datum/file/program/ntos - -/obj/machinery/computer3/proc/disassemble(mob/user as mob) // todo - return - - -/obj/structure/computer3frame - density = 1 - anchored = 0 - name = "computer frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "0" - var/state = 0 - - var/obj/item/part/computer/circuitboard/circuit = null - var/completed = /obj/machinery/computer - - // Computer3 components - a carbon copy of the list from - // computer.dm; however, we will need to check to make sure - // we don't install more components than the computer frame - // can handle. This will be different for certain formfactors. - - var/max_components = 4 - var/list/components = list() - - // Storage - var/obj/item/part/computer/storage/hdd/hdd = null - var/obj/item/part/computer/storage/removable/floppy = null - // Networking - var/obj/item/part/computer/networking/radio/radio = null // not handled the same as other networks - var/obj/item/part/computer/networking/cameras/camnet = null // just plain special - var/obj/item/part/computer/networking/net = null // Proximity, area, or cable network - var/obj/item/part/computer/networking/subspace/centcom = null // only for offstation communications - - // Card reader - note the HoP reader is a subtype - var/obj/item/part/computer/cardslot/cardslot = null - - // Misc & special purpose - var/obj/item/part/computer/ai_holder/cradle = null - var/obj/item/part/computer/toybox/toybox = null - - // Battery must be installed BEFORE wiring the computer. - // if installing it in an existing computer, you will have to - // get back to this state first. - var/obj/item/weapon/cell/battery = null - -/obj/structure/computer3frame/server - name = "server frame" - completed = /obj/machinery/computer3/server - max_components = 6 -/obj/structure/computer3frame/wallcomp - name = "wall-computer frame" - completed = /obj/machinery/computer3/wall_comp - max_components = 3 -/obj/structure/computer3frame/laptop - name = "laptop frame" - completed = /obj/machinery/computer3/laptop - max_components = 3 - -/obj/structure/computer3frame/attackby(obj/item/P as obj, mob/user as mob) - switch(state) - if(0) - if(P.is_wrench()) - playsound(src.loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed)) - to_chat(user, "You wrench the frame into place.") - src.anchored = 1 - src.state = 1 - if(istype(P, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = P - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(src.loc, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - new /obj/item/stack/material/steel( src.loc, 5 ) - qdel(src) - if(1) - if(P.is_wrench()) - playsound(src.loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed)) - to_chat(user, "You unfasten the frame.") - src.anchored = 0 - src.state = 0 - if(istype(P, /obj/item/weapon/circuitboard) && !circuit) - var/obj/item/weapon/circuitboard/B = P - if(B.board_type == "computer") - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - src.icon_state = "1" - src.circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(P.is_screwdriver() && circuit) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - src.state = 2 - src.icon_state = "2" - if(P.is_crowbar() && circuit) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - src.state = 1 - src.icon_state = "0" - circuit.loc = src.loc - src.circuit = null - if(2) - if(P.is_screwdriver() && circuit) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - src.state = 1 - src.icon_state = "1" - - if(P.is_crowbar()) - if(battery) - playsound(src.loc, P.usesound, 50, 1) - if(do_after(10 * P.toolspeed)) - battery.loc = loc - to_chat(user, "You remove [battery].") - battery = null - else - to_chat(user, "There's no battery to remove!") - - if(istype(P, /obj/item/weapon/cell)) - if(!battery) - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(5)) - battery = P - P.loc = src - to_chat(user, "You insert [battery].") - else - to_chat(user, "There's already \an [battery] in [src]!") - - - if(istype(P, /obj/item/stack/cable_coil)) - if(P:amount >= 5) - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20)) - if(P) - P:amount -= 5 - if(!P:amount) qdel(P) - to_chat(user, "You add cables to the frame.") - src.state = 3 - src.icon_state = "3" - if(3) - if(P.is_wirecutter()) - if(components.len) - to_chat(user, "There are parts in the way!") - return - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - src.state = 2 - src.icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) - A.amount = 5 - - if(P.is_crowbar()) - remove_peripheral() - - if(istype(P, /obj/item/stack/material) && P.get_material_name() == "glass") - var/obj/item/stack/S = P - if(S.amount >= 2) - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20)) - if(S) - S.use(2) - to_chat(user, "You put in the glass panel.") - src.state = 4 - src.icon_state = "4" - if(4) - if(P.is_crowbar()) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - src.state = 3 - src.icon_state = "3" - new /obj/item/stack/material/glass( src.loc, 2 ) - if(P.is_screwdriver()) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/obj/machinery/computer3/B = new src.circuit.build_path ( src.loc, built=1 ) - /*if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - //if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - if(istype(circuit,/obj/item/weapon/circuitboard/supplycomp)) - var/obj/machinery/computer/supplycomp/SC = B - var/obj/item/weapon/circuitboard/supplycomp/C = circuit - SC.can_order_contraband = C.contraband_enabled*/ - B.circuit = circuit - circuit.loc = B - if(circuit.OS) - circuit.OS.computer = B - B.RefreshParts() // todo - qdel(src) - -/* - This will remove peripherals if you specify one, but the main function is to - allow the user to remove a part specifically. -*/ -/obj/structure/computer3frame/proc/remove_peripheral(var/obj/item/I = null) - if(!components || !components.len) - to_chat(usr, "There are no components in [src] to take out!") - return 0 - if(!I) - I = input(usr, "Remove which component?","Remove component", null) as null|obj in components - - if(I) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - if(do_after(usr,25)) - if(I==hdd) - components -= hdd - hdd.loc = loc - hdd = null - else if(I==floppy) - components -= floppy - floppy.loc = loc - floppy = null - else if(I==radio) - components -= radio - radio.loc = loc - radio = null - else if(I==camnet) - components -= camnet - camnet.loc = loc - camnet = null - else if(I==net) - components -= net - net.loc = loc - net = null - else if(I==cradle) - components -= cradle - cradle.loc = loc - cradle = null - else if(I==toybox) - components -= toybox - toybox.loc = loc - toybox = null - else - warning("Erronous component in computerframe/remove_peripheral: [I]") - I.loc = loc - to_chat(usr, "You remove [I]") - return 1 - return 0 - -/obj/structure/computer3frame/proc/insert_peripheral(var/obj/item/I) - if(components.len >= max_components) - to_chat(usr, "There isn't room in [src] for another component!") - return 0 - switch(I.type) - if(/obj/item/part/computer/storage/hdd) - if(hdd) - to_chat(usr, "There is already \an [hdd] in [src]!") - return 0 - hdd = I - components += hdd - hdd.loc = src - if(/obj/item/part/computer/storage/removable) - if(floppy) - to_chat(usr, "There is already \an [floppy] in [src]!") - return 0 - floppy = I - components += floppy - floppy.loc = src - if(/obj/item/part/computer/networking/radio) - if(radio) - to_chat(usr, "There is already \an [radio] in [src]!") - return 0 - radio = I - components += radio - radio.loc = src - if(/obj/item/part/computer/networking/cameras) - if(camnet) - to_chat(usr, "There is already \an [camnet] in [src]!") - return 0 - camnet = I - components += camnet - camnet.loc = src - if(/obj/item/part/computer/networking) - if(net) - to_chat(usr, "There is already \an [net] in [src]!") - - diff --git a/code/game/machinery/computer3/component.dm b/code/game/machinery/computer3/component.dm deleted file mode 100644 index 3c8d972825..0000000000 --- a/code/game/machinery/computer3/component.dm +++ /dev/null @@ -1,230 +0,0 @@ - -/* - Objects used to construct computers, and objects that can be inserted into them, etc. - - TODO: - * Synthesizer part (toybox, injectors, etc) -*/ - - - -/obj/item/part/computer - name = "computer part" - desc = "Holy jesus you donnit now" - gender = PLURAL - icon = 'icons/obj/stock_parts.dmi' - icon_state = "hdd1" - w_class = ITEMSIZE_SMALL - - var/emagged = 0 - - // the computer that this device is attached to - var/obj/machinery/computer3/computer - - // If the computer is attacked by an item it will reference this to decide which peripheral(s) are affected. - var/list/attackby_types = list() - -/obj/item/part/computer/proc/allow_attackby(var/obj/item/I, var/mob/user) - for(var/typepath in attackby_types) - if(istype(I, typepath)) - return 1 - return 0 - -/obj/item/part/computer/proc/init(var/obj/machinery/computer/target) - computer = target - // continue to handle all other type-specific procedures - -/* - Below are all the miscellaneous components - For storage drives, see storage.dm - For networking parts, see -*/ - -/obj/item/part/computer/ai_holder - name = "intelliCore computer module" - desc = "Contains a specialized nacelle for dealing with highly sensitive equipment without interference." - - attackby_types = list(/obj/item/device/aicard) - - var/mob/living/silicon/ai/occupant = null - var/busy = 0 - -/obj/item/part/computer/ai_holder/attackby(obj/I as obj,mob/user as mob) - if(computer && !computer.stat) - if(istype(I, /obj/item/device/aicard)) - var/obj/item/device/aicard/card = I - var/mob/living/silicon/ai/comp_ai = locate() in src - var/mob/living/silicon/ai/card_ai = locate() in card - - if(istype(comp_ai)) - if(busy) - to_chat(user, "ERROR: Reconstruction in progress.") - return - - if(card.grab_ai(comp_ai, user)) - occupant = null - - else if(istype(card_ai)) - load_ai(card_ai,card,user) - - if(computer.program) - computer.program.update_icon() - - computer.update_icon() - ..() - return - -/obj/item/part/computer/ai_holder/proc/load_ai(var/mob/living/silicon/ai/transfer, var/obj/item/device/aicard/card, var/mob/user) - - if(!istype(transfer)) - return - - // Transfer over the AI. - to_chat(transfer, "You have been transferred into a mobile terminal. Sadly, there is no remote access from here.") - to_chat(user, "Transfer successful: [transfer.name] placed within mobile terminal.") - - transfer.loc = src - transfer.cancel_camera() - transfer.control_disabled = 1 - occupant = transfer - - if(card) - card.clear() - - -/* - ID computer cardslot - reading and writing slots -*/ - -/obj/item/part/computer/cardslot - name = "magnetic card slot" - desc = "Contains a slot for reading magnetic swipe cards." - - var/obj/item/weapon/card/reader = null - - attackby_types = list(/obj/item/weapon/card) - -/obj/item/part/computer/cardslot/attackby(var/obj/item/I as obj, var/mob/user) - if(istype(I,/obj/item/weapon/card) && computer) - if(istype(I,/obj/item/weapon/card/emag) && !reader) // emag reader slot - user.visible_message("[computer]'s screen flickers for a moment.","You insert \the [I]. After a moment, the card ejects itself, and [computer] beeps.","[computer] beeps.") - computer.emagged = 1 - return - - insert(I, user) - return - ..(I,user) - - // cardslot.insert(card, slot) - // card: The card obj you want to insert (usually your ID) - // user: The mob inserting the card -/obj/item/part/computer/cardslot/proc/insert(var/obj/item/weapon/card/card, var/mob/user) - if(equip_to_reader(card, user)) - to_chat(user, "You insert the card into reader slot") - return 1 - to_chat(user, "There is already something in the reader slot.") - return 0 - - - // Usage of insert() preferred, as it also tells result to the user. -/obj/item/part/computer/cardslot/proc/equip_to_reader(var/obj/item/weapon/card/card, var/mob/living/L) - if(!reader) - L.drop_item() - card.loc = src - reader = card - return 1 - return 0 - - // cardslot.remove(slot) - // user: The mob removing the card -/obj/item/part/computer/cardslot/proc/remove(var/mob/user) - if(remove_reader(user)) - to_chat(user, "You remove the card from reader slot") - return 1 - to_chat(user, "There is nothing in the reader slot") - return 0 - -/obj/item/part/computer/cardslot/proc/remove_reader(var/mob/living/L) - if(reader) - if(ishuman(L) && !L.get_active_hand()) - L.put_in_hands(reader) - else - reader.loc = get_turf(computer) - reader = null - return 1 - return 0 - - // Authorizes the user based on the computer's requirements -/obj/item/part/computer/cardslot/proc/authenticate() - return computer.check_access(reader) - - -/obj/item/part/computer/cardslot/dual - name = "magnetic card reader" - desc = "Contains slots for inserting magnetic swipe cards for reading and writing." - - var/obj/item/weapon/card/writer = null - - - // Ater: Single- and dual-slot card readers have separate functions. - // According to OOP principles, they should be separate classes and use inheritance, polymorphism. - - -/obj/item/part/computer/cardslot/dual/proc/equip_to_writer(var/obj/item/weapon/card/card, var/mob/living/L) - if(!writer) - L.drop_item() - card.loc = src - writer = card - return 1 - return 0 - -/obj/item/part/computer/cardslot/dual/proc/remove_from_writer(var/mob/living/L) - if(writer) - if(ishuman(L) && !L.get_active_hand()) - L.put_in_hands(writer) - else - writer.loc = get_turf(computer) - writer = null - return 1 - return 0 - - // cardslot.insert(card, slot) - // card: The card obj you want to insert (usually your ID) - // user: The mob inserting the card - // slot: Which slot to insert into (1->Reader, 2->Writer, 3->Auto) Default 3 -/obj/item/part/computer/cardslot/dual/insert(var/obj/item/weapon/card/card, var/mob/user, var/slot = 3) - if(slot != 2) - if(..(card, user)) - return 1 - - if(slot != 1) - if(equip_to_writer(card, user)) - to_chat(user, "You insert the card into writer slot") - return 1 - else - to_chat(user, "There is already something in the writer slot.") - return 0 - - // cardslot/dual.insert(card, slot) - // user: The mob removing the card - // slot: Which slot to remove from (1->Reader, 2->Writer, 3->Both, 4->Reader and if empty, Writer) Default 3 -/obj/item/part/computer/cardslot/dual/remove(var/mob/user, var/slot = 3) - if(slot != 2) - if(..(user) && slot != 3) // ..() probes reader - return 1 // slot is either 1 or 4, where we only probe reader if there's anything in it - - if(slot != 1) // If slot is 1, then we only probe reader - if(remove_from_writer(user)) // Probe writer - to_chat(user, "You remove the card from the writer slot") - return 1 - to_chat(user, "There is nothing in the writer slot.") - return 0 - -/obj/item/part/computer/cardslot/dual/proc/addfile(var/datum/file/F) - if(!istype(writer,/obj/item/weapon/card/data)) - return 0 - var/obj/item/weapon/card/data/D = writer - if(D.files.len > 3) - return 0 - D.files += F - return 1 diff --git a/code/game/machinery/computer3/computer.dm b/code/game/machinery/computer3/computer.dm deleted file mode 100644 index c9d8eb22de..0000000000 --- a/code/game/machinery/computer3/computer.dm +++ /dev/null @@ -1,459 +0,0 @@ -/obj/machinery/computer3 - name = "computer" - icon = 'icons/obj/computer3.dmi' - icon_state = "frame" - density = 1 - anchored = 1.0 - - idle_power_usage = 20 - active_power_usage = 50 - - var/allow_disassemble = 1 - var/legacy_icon = 0 // if 1, use old style icons - var/show_keyboard = 1 - - // These is all you should need to change when creating a new computer. - // If there is no default program, the OS will run instead. - // If there is no hard drive, but there is a default program, the OS rom on - // the circuitboard will be overridden. - - // For these, typepaths are used, NOT objects - - var/default_prog = null // the program running when spawned - var/list/spawn_files = list() // files added when spawned - var/list/spawn_parts = list(/obj/item/part/computer/storage/hdd/big) // peripherals to spawn - - // Computer3 components - put an object in them in New() when not built - // I used to have a more pliable /list, but the ambiguities - // there in how many of what you had was killing me, especially - // when you had to search the list to find what you had. - - // Mostly decorative, holds the OS rom - var/obj/item/part/computer/circuitboard/circuitb - - // Storage - var/obj/item/part/computer/storage/hdd/hdd = null - var/obj/item/part/computer/storage/removable/floppy = null - // Networking - var/obj/item/part/computer/networking/radio/radio = null // not handled the same as other networks - var/obj/item/part/computer/networking/cameras/camnet = null // just plain special - var/obj/item/part/computer/networking/net = null // Proximity, area, or cable network - - // Card reader - note the HoP reader is a subtype - var/obj/item/part/computer/cardslot/cardslot = null - - // Misc & special purpose - var/obj/item/part/computer/ai_holder/cradle = null - var/obj/item/part/computer/toybox/toybox = null - - - // Legacy variables - // camera networking - overview (???) - var/mapping = 0 - var/last_pic = 1.0 - - // Purely graphical effect - var/icon/kb = null - - // These are necessary in order to consolidate all computer types into one - var/datum/wires/wires = null - var/powernet = null - - // Used internally - var/datum/file/program/program = null // the active program (null if defaulting to os) - var/datum/file/program/os = null // the base code of the machine (os or hardcoded program) - - // If you want the computer to have a UPS, add a battery during construction. This is useful for things like - // the comms computer, solar trackers, etc, that should function when all else is off. - // Laptops will require batteries and have no mains power. - - var/obj/item/weapon/cell/battery = null // uninterruptible power supply aka battery - -/obj/machinery/computer3/New(var/L, var/built = 0) - ..() - spawn(2) - power_change() - - if(show_keyboard) - var/kb_state = "kb[rand(1,15)]" - kb = image('icons/obj/computer3.dmi',icon_state=kb_state) - overlays += kb - - if(!built) - if(!circuitb || !istype(circuitb)) - circuitb = new(src) - if(circuitb.OS) - os = circuitb.OS - circuitb.OS.computer = src - else - os = null - - // separated into its own function because blech - spawn_parts() - - if(default_prog) // Add the default software if applicable - var/datum/file/program/P = new default_prog - if(hdd) - hdd.addfile(P,1) - program = P - if(!os) - os = P - else if(floppy) - floppy.inserted = new(floppy) - floppy.files = floppy.inserted.files - floppy.addfile(P) - program = P - else - circuitb.OS = P - circuitb.OS.computer = src - os = circuitb.OS - circuitb.name = "Circuitboard ([P])" - - if(hdd) // Spawn files - for(var/typekey in spawn_files) - hdd.addfile(new typekey,1) - - update_icon() - -/obj/machinery/computer3/verb/ResetComputer() - set name = "Reset Computer" - set category = "Object" - set src in view(1) - - if(usr.stat || usr.restrained() || usr.lying || !istype(usr, /mob/living)) - to_chat(usr, "You can't do that.") - return - - if(!Adjacent(usr)) - to_chat(usr, "You can't reach it.") - return - - Reset() - -/obj/machinery/computer3/proc/update_spawn_files() - for(var/typekey in spawn_files) - hdd.addfile(new typekey,1) - -/obj/machinery/computer3/proc/spawn_parts() - for(var/typekey in spawn_parts) - if(ispath(typekey,/obj/item/part/computer/storage/removable)) - if(floppy) - continue - floppy = new typekey(src) - floppy.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/storage/hdd)) - if(hdd) - continue - hdd = new typekey(src) - hdd.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/networking/cameras)) - if(camnet) - continue - camnet = new typekey(src) - camnet.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/networking/radio)) - if(radio) - continue - radio = new typekey(src) - radio.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/networking)) - if(net) - continue - net = new typekey(src) - net.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/cardslot)) - if(cardslot) - continue - cardslot = new typekey(src) - cardslot.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/ai_holder)) - if(cradle) - continue - cradle = new typekey(src) - cradle.init(src) - continue - - if(ispath(typekey,/obj/item/part/computer/toybox)) - if(toybox) - continue - toybox = new typekey(src) - toybox.init(src) - continue - - if(ispath(typekey,/obj/item/weapon/cell)) - if(battery) - continue - battery = new typekey(src) - continue - -/obj/machinery/computer3/proc/Reset(var/error = 0) - for(var/mob/living/M in range(1)) - M << browse(null,"window=\ref[src]") - if(program) - program.Reset() - program = null - req_access = os.req_access - update_icon() - - // todo does this do enough - -/obj/machinery/computer3/emp_act(severity) - if(prob(20/severity)) set_broken() - ..() - - -/obj/machinery/computer3/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(25)) - qdel(src) - return - if (prob(50)) - for(var/x in verbs) - verbs -= x - set_broken() - if(3.0) - if (prob(25)) - for(var/x in verbs) - verbs -= x - set_broken() - else - return - - /* - Computers have the capability to use a battery backup. - Note that auto_use_power's return value is strictly whether - or not it is successfully powered. - - This allows laptops, and also allows you to create computers that - remain active when: - - * the APC is destroy'd, emag'd, malf'd, emp'd, ninja'd etc - * the computer was built in an unpowered zone - * the station power is out, cables are cut, etc - - By default, most computers will NOT spawn with a battery backup, and - SHOULD not. Players can take apart a computer to insert the battery - if they want to ensure, for example, the AI upload remains when the - power is cut off. - - Make sure to use use_power() a bunch in peripherals code - */ -/obj/machinery/computer3/auto_use_power() - if(!powered(power_channel)) - if(battery && battery.charge > 0) - if(use_power == 1) - battery.use(idle_power_usage) - else - battery.use(active_power_usage) - return 1 - 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/computer3/use_power(var/amount, var/chan = -1) - if(chan == -1) - chan = power_channel - - var/area/A = get_area(loc) - if(istype(A) && A.powered(chan)) - A.use_power(amount, chan) - else if(battery && battery.charge > 0) - battery.use(amount) - -/obj/machinery/computer3/power_change() - if( !powered(power_channel) && (!battery || battery.charge <= 0) ) - stat |= NOPOWER - else - stat &= ~NOPOWER - -/obj/machinery/computer3/process() - auto_use_power() - power_change() - update_icon() - if(stat & (NOPOWER|BROKEN)) - return - - if(program) - program.process() - return - - if(os) - program = os - os.process() - return - -/obj/machinery/computer3/proc/set_broken() - icon_state = "computer_b" - stat |= BROKEN - if(program) - program.error = BUSTED_ASS_COMPUTER - if(os) - os.error = BUSTED_ASS_COMPUTER - -/obj/machinery/computer3/attackby(obj/item/I as obj, mob/user as mob) - if(I.is_screwdriver() && allow_disassemble) - disassemble(user) - return - - /* - +++++++++++ - |IMPORTANT| If you add a peripheral, put it in this list - +++++++++++ -------------------------------------------- - */ - - var/list/p_list = list() - for(var/obj/item/part/computer/C in src) - if(!isnull(C) && C.allow_attackby(I,user)) - p_list += C - if(p_list.len) - var/obj/item/part/computer/P = null - if(p_list.len == 1) - P = p_list[1] - else - P = input(user,"Which component?") as null|anything in p_list - - if(P) - P.attackby(I,user) - return - ..() - -/obj/machinery/computer3/attack_hand(var/mob/user as mob) - if(stat) - Reset() - return - - // I don't want to deal with computers that you can't walk up to and use - // there is still cardauth anyway - //if(!allowed(user)) - // return - - if(program) - if(program.computer != src) // floppy disk may have been removed, etc - Reset() - attack_hand(user) - return - if(program.error) - Crash(program.error) - return - user.set_machine(src) - program.attack_hand(user) // will normally translate to program/interact() - return - - if(os) - program = os - user.set_machine(src) - os.attack_hand(user) - return - - to_chat(user, "\The [src] won't boot!") - -/obj/machinery/computer3/attack_ai(var/mob/user as mob) // copypasta because server racks lose attack_hand() - if(stat) - Reset() - return - - if(program) - if(program.computer != src) // floppy disk may have been removed, etc - Reset() - attack_ai(user) - return - if(program.error) - Crash(program.error) - return - user.set_machine(src) - program.attack_hand(user) // will normally translate to program/interact() - return - - if(os) - program = os - user.set_machine(src) - os.attack_hand(user) - return - - to_chat(user, "\The [src] won't boot!") - -/obj/machinery/computer3/interact() - if(stat) - Reset() - return - if(!allowed(usr) || !usr in view(1)) - usr.unset_machine() - return - - if(program) - program.interact() - return - - if(os) - program = os - os.interact() - return - -/obj/machinery/computer3/update_icon() - if(legacy_icon) - icon_state = initial(icon_state) - // Broken - if(stat & BROKEN) - icon_state += "b" - - // Powered - else if(stat & NOPOWER) - icon_state = initial(icon_state) - icon_state += "0" - return - if(stat) - overlays.Cut() - return - if(program) - overlays = list(program.overlay) - if(show_keyboard) - overlays += kb - name = "[program.name] [initial(name)]" - else if(os) - overlays = list(os.overlay) - if(show_keyboard) - overlays += kb - name = initial(name) - else - var/global/image/generic = image('icons/obj/computer3.dmi',icon_state="osod") // orange screen of death - overlays = list(generic) - if(show_keyboard) - overlays += kb - name = initial(name) + " (orange screen of death)" - -//Returns percentage of battery charge remaining. Returns -1 if no battery is installed. -/obj/machinery/computer3/proc/check_battery_status() - if (battery) - var/obj/item/weapon/cell/B = battery - return round(B.charge / (B.maxcharge / 100)) - else - return -1 - - - -/obj/machinery/computer3/wall_comp - name = "terminal" - icon = 'icons/obj/computer3.dmi' - icon_state = "wallframe" - density = 0 - pixel_y = -3 - show_keyboard = 0 diff --git a/code/game/machinery/computer3/computer3_notes.dm b/code/game/machinery/computer3/computer3_notes.dm deleted file mode 100644 index c27410e637..0000000000 --- a/code/game/machinery/computer3/computer3_notes.dm +++ /dev/null @@ -1,34 +0,0 @@ -/* - 1. Do NOT confuse Computer.Crash(errorcode) with byond CRASH(message) - 2 Do NOT talk about fight club. - 3 If this if your first night here, you have to crash the computer. - 4 Where am I? - 5 Someone help me, please... - 6. Be sure to use computer.use_power() appropriately. Laptops should run out of battery occasionally. - 7 Everyone fights, no-one quits. If you don't do your job, I'll crash you myself. - 6 Don't allow more than 42 angels to dance on the head of a pin. - 5. Once a computer has spawned, they are just like the rest, except when they aren't. - 4 Get me four glasses of apple juice. - 3. Components are only added or removed when disassembled and rebuilt. However, they may be EMP'd. - 2 Only you can prevent friendly fire. - 1 Do not talk about fight club. - 2. If a component subtype needs to be handled separately (removable drives, radio networks), declare it separately. - 3 Television rules the nation - 4. interactable() does all the sanity checks, adds fingerprints, sets machines, initializes popup, and makes a damn fine pot of coffee. - 5 Love conquers all. - 6 If at all possible, do a barrel roll. - 7. Don't forget to use the network verify function to make sure you still have access to remote machines. - - - - - TODO: - * "Nothing left to call the shuttle" check - * Communications terminal printing - move it to a printer of some sort? Make a printer peripheral--but then which ones print the comms? - * Remove the partially transparent border on program screens, as it clashes with some frames - * Chop the corners on program screens now that screen sizes are standard - * ntos: - * Needs a text editor/viewer - * Needs file copy and file move - I think I know how I'm gonna do it - * Needs a peripheral view (eject disks and cards, network actions, ???) -*/ \ No newline at end of file diff --git a/code/game/machinery/computer3/computers/HolodeckControl.dm b/code/game/machinery/computer3/computers/HolodeckControl.dm deleted file mode 100644 index da3c2416fd..0000000000 --- a/code/game/machinery/computer3/computers/HolodeckControl.dm +++ /dev/null @@ -1,245 +0,0 @@ -/obj/machinery/computer3/HolodeckControl - default_prog = /datum/file/program/holodeck - - -// Todo: I personally would like to add a second holodeck in the theater for making appropriate playgrounds. -// perhaps a holodeck association keyfile? -// One more thing while I'm here -// C3 allows multiple computers to run this program, but it was designed on the assumption that only one would, ever -// I am not going to add or remove anything right now, I'm just porting it - - -/datum/file/program/holodeck - name = "Holodeck Control Console" - desc = "Used to control a nearby holodeck." - active_state = "holocontrol" - var/area/linkedholodeck = null - var/area/target = null - var/active = 0 - var/list/holographic_items = list() - var/damaged = 0 - var/last_change = 0 - var/emagged = 0 - - -/datum/file/program/holodeck/interact() - if(!interactable()) - return - var/dat = "

    Current Loaded Programs

    " - dat += "((Empty Court))
    " - dat += "((Boxing Court))
    " - dat += "((Basketball Court))
    " - dat += "((Thunderdome Court))
    " - dat += "((Beach))
    " -// dat += "((Shutdown System))
    " - - dat += "Please ensure that only holographic weapons are used in the holodeck if a combat simulation has been loaded.
    " - - if(emagged) - dat += "(Begin Atmospheric Burn Simulation)
    " - dat += "Ensure the holodeck is empty before testing.
    " - dat += "
    " - dat += "(Begin Wildlife Simulation)
    " - dat += "Ensure the holodeck is empty before testing.
    " - dat += "
    " - if(issilicon(usr)) - dat += "(Re-Enable Safety Protocols?)
    " - dat += "Safety Protocols are DISABLED
    " - else - if(issilicon(usr)) - dat += "(Override Safety Protocols?)
    " - dat += "
    " - dat += "Safety Protocols are ENABLED
    " - - popup.set_content(dat) - popup.open() - return - -/datum/file/program/holodeck/Topic(var/href, var/list/href_list) - if(!interactable() || ..(href,href_list)) - return - - if("emptycourt" in href_list) - target = locate(/area/holodeck/source_emptycourt) - if(target) - loadProgram(target) - - else if("boxingcourt" in href_list) - target = locate(/area/holodeck/source_boxingcourt) - if(target) - loadProgram(target) - - else if("basketball" in href_list) - target = locate(/area/holodeck/source_basketball) - if(target) - loadProgram(target) - - else if("thunderdomecourt" in href_list) - target = locate(/area/holodeck/source_thunderdomecourt) - if(target) - loadProgram(target) - - else if("beach" in href_list) - target = locate(/area/holodeck/source_beach) - if(target) - loadProgram(target) - - else if("turnoff" in href_list) - target = locate(/area/holodeck/source_plating) - if(target) - loadProgram(target) - - else if("burntest" in href_list) - if(!emagged) - return - target = locate(/area/holodeck/source_burntest) - if(target) - loadProgram(target) - - else if("wildlifecarp" in href_list) - if(!emagged) - return - target = locate(/area/holodeck/source_wildlife) - if(target) - loadProgram(target) - - else if("AIoverride" in href_list) - if(!issilicon(usr)) - return - emagged = !emagged - if(emagged) - message_admins("[key_name_admin(usr)] overrode the holodeck's safeties") - log_game("[key_name(usr)] overrided the holodeck's safeties") - else - message_admins("[key_name_admin(usr)] restored the holodeck's safeties") - log_game("[key_name(usr)] restored the holodeck's safeties") - - interact() - return - -/datum/file/program/holodeck/Reset() - emergencyShutdown() - -/datum/file/program/holodeck/process() - if(active) - if(!checkInteg(linkedholodeck)) - damaged = 1 - target = locate(/area/holodeck/source_plating) - if(target) - loadProgram(target) - active = 0 - for(var/mob/M in range(10,src)) - M.show_message("The holodeck overloads!") - - for(var/turf/T in linkedholodeck) - if(prob(30)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, T) - s.start() - T.ex_act(3) - T.hotspot_expose(1000,500,1) - - for(var/item in holographic_items) - if(!(get_turf(item) in linkedholodeck)) - derez(item, 0) - -/datum/file/program/holodeck/proc/derez(var/obj/obj , var/silent = 1) - holographic_items.Remove(obj) - - if(obj == null) - return - - if(isobj(obj)) - var/mob/M = obj.loc - if(ismob(M)) - M.remove_from_mob(obj) - - if(!silent) - var/obj/oldobj = obj - obj.visible_message("The [oldobj.name] fades away!") - qdel(obj) - -/datum/file/program/holodeck/proc/checkInteg(var/area/A) - for(var/turf/T in A) - if(istype(T, /turf/space)) - return 0 - return 1 - -/datum/file/program/holodeck/proc/togglePower(var/toggleOn = 0) - if(toggleOn) - var/area/targetsource = locate(/area/holodeck/source_emptycourt) - holographic_items = targetsource.copy_contents_to(linkedholodeck) - - spawn(30) - for(var/obj/effect/landmark/L in linkedholodeck) - if(L.name=="Atmospheric Test Start") - spawn(20) - var/turf/T = get_turf(L) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, T) - s.start() - if(T) - T.temperature = 5000 - T.hotspot_expose(50000,50000,1) - active = 1 - else - for(var/item in holographic_items) - derez(item) - var/area/targetsource = locate(/area/holodeck/source_plating) - targetsource.copy_contents_to(linkedholodeck , 1) - active = 0 - -/datum/file/program/holodeck/proc/loadProgram(var/area/A) - if(world.time < (last_change + 25)) - if(world.time < (last_change + 15))//To prevent super-spam clicking, reduced process size and annoyance -Sieve - return - for(var/mob/M in range(3,src)) - M.show_message("ERROR. Recalibrating projetion apparatus.") - last_change = world.time - return - - last_change = world.time - active = 1 - - for(var/item in holographic_items) - derez(item) - - for(var/obj/effect/decal/cleanable/blood/B in linkedholodeck) - qdel(B) - - for(var/mob/living/simple_mob/animal/space/carp/C in linkedholodeck) - qdel(C) - - holographic_items = A.copy_contents_to(linkedholodeck , 1) - - if(emagged) - for(var/obj/item/weapon/holo/esword/H in linkedholodeck) - H.damtype = BRUTE - - spawn(30) - for(var/obj/effect/landmark/L in linkedholodeck) - if(L.name=="Atmospheric Test Start") - spawn(20) - var/turf/T = get_turf(L) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, T) - s.start() - if(T) - T.temperature = 5000 - T.hotspot_expose(50000,50000,1) - if(L.name=="Holocarp Spawn") - new /mob/living/simple_mob/animal/space/carp(L.loc) - - -/datum/file/program/holodeck/proc/emergencyShutdown() - //Get rid of any items - for(var/item in holographic_items) - derez(item) - //Turn it back to the regular non-holographic room - target = locate(/area/holodeck/source_plating) - if(target) - loadProgram(target) - - var/area/targetsource = locate(/area/holodeck/source_plating) - targetsource.copy_contents_to(linkedholodeck , 1) - active = 0 diff --git a/code/game/machinery/computer3/computers/Operating.dm b/code/game/machinery/computer3/computers/Operating.dm deleted file mode 100644 index 0f989780d7..0000000000 --- a/code/game/machinery/computer3/computers/Operating.dm +++ /dev/null @@ -1,47 +0,0 @@ -/obj/machinery/computer3/operating - default_prog = /datum/file/program/op_monitor - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/prox) - icon_state = "frame-med" - -/datum/file/program/op_monitor - name = "operating table monitor" - desc = "Monitors patient status during surgery." - active_state = "operating" - var/mob/living/carbon/human/patient = null - var/obj/machinery/optable/table = null - - -/datum/file/program/op_monitor/interact() - if(!interactable()) - return - if(!computer.net) - computer.Crash(MISSING_PERIPHERAL) - return - table = computer.net.connect_to(/obj/machinery/optable,table) - - var/dat = "" - if(table) - dat += "Patient information:
    " - if(src.table && (src.table.check_victim())) - src.patient = src.table.victim - dat += {"Patient Status: [patient.stat ? "Non-Responsive" : "Stable"]
    - Blood Type: [patient.b_type]
    -
    - Health: [round(patient.health)]
    - Brute Damage: [round(patient.getBruteLoss())]
    - Toxins Damage: [round(patient.getToxLoss())]
    - Fire Damage: [round(patient.getFireLoss())]
    - Suffocation Damage: [round(patient.getOxyLoss())]
    - "} - else - src.patient = null - dat += "No patient detected" - else - dat += "Operating table not found." - - popup.set_content(dat) - popup.open() -/datum/file/program/op_monitor/Topic() - if(!interactable()) - return - ..() \ No newline at end of file diff --git a/code/game/machinery/computer3/computers/arcade.dm b/code/game/machinery/computer3/computers/arcade.dm deleted file mode 100644 index 6b235659cf..0000000000 --- a/code/game/machinery/computer3/computers/arcade.dm +++ /dev/null @@ -1,183 +0,0 @@ -/obj/machinery/computer3/arcade - default_prog = /datum/file/program/arcade - spawn_parts = list(/obj/item/part/computer/toybox) //NO HDD - the game is loaded on the circuitboard's OS slot - -/obj/item/part/computer/toybox - var/list/prizes = list( /obj/item/weapon/storage/box/snappops = 2, - /obj/item/toy/blink = 2, - /obj/item/clothing/under/syndicate/tacticool = 2, - /obj/item/toy/sword = 2, - /obj/item/weapon/gun/projectile/revolver/capgun = 2, - /obj/item/toy/crossbow = 2, - /obj/item/clothing/suit/syndicatefake = 2, - /obj/item/weapon/storage/fancy/crayons = 2, - /obj/item/toy/spinningtoy = 2, - /obj/item/toy/prize/ripley = 1, - /obj/item/toy/prize/fireripley = 1, - /obj/item/toy/prize/deathripley = 1, - /obj/item/toy/prize/gygax = 1, - /obj/item/toy/prize/durand = 1, - /obj/item/toy/prize/honk = 1, - /obj/item/toy/prize/marauder = 1, - /obj/item/toy/prize/seraph = 1, - /obj/item/toy/prize/mauler = 1, - /obj/item/toy/prize/odysseus = 1, - /obj/item/toy/prize/phazon = 1 - ) - -/obj/item/part/computer/toybox/allow_attackby(var/obj/item/I, var/mob/user) - return 0 - -/obj/item/part/computer/toybox/proc/dispense() - if(computer && !computer.stat) - var/prizeselect = pickweight(prizes) - new prizeselect(computer.loc) - if(istype(prizeselect, /obj/item/weapon/gun/projectile/revolver/capgun)) //Ammo comes with the gun - new /obj/item/projectile/bullet/pistol/cap(src.loc) - else if(istype(prizeselect, /obj/item/clothing/suit/syndicatefake)) //Helmet is part of the suit - new /obj/item/clothing/head/syndicatefake(computer.loc) - feedback_inc("arcade_win_normal") - computer.use_power(500) - -/datum/file/program/arcade - desc = "The best arcade game ever produced by the Company's short-lived entertainment divison." - //headcanon: they also ported E.T. for the atari 2600, superman 64, and basically every other movie tie-in game ever - - active_state = "generic" - - var/turtle = 0 - var/enemy_name = "Space Villian" - var/temp = "Winners Don't Use Spacedrugs" //Temporary message, for attack messages, etc - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = 0 - var/blocked = 0 //Player cannot attack/heal while set - -/datum/file/program/arcade/New() - ..() - var/name_action - var/name_part1 - var/name_part2 - - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "ERP ") - - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn") - - enemy_name = replacetext(name_part1, "the ", "") + name_part2 - name = (name_action + name_part1 + name_part2) - -/datum/file/program/arcade/interact() - if(!interactable()) - return - var/dat// = topic_link(src,"close","Close") - dat = "

    [enemy_name]

    " - - dat += "

    [temp]

    " - dat += "
    Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]
    " - dat += "
    " - - if (gameover) - dat += "[topic_link(src,"newgame","New Game")]" - else - dat += "[topic_link(src,"attack","Attack")] | [topic_link(src,"heal","Heal")] | [topic_link(src,"charge","Recharge Power")]" - - dat += "
    " - - popup.set_content(dat) - popup.open() - -/datum/file/program/arcade/Topic(href, list/href_list) - if(!interactable() || ..(href,href_list)) - return - if (!blocked && !gameover) - if ("attack" in href_list) - blocked = 1 - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - computer.updateUsrDialog() - if(turtle > 0) - turtle-- - - sleep(10) - enemy_hp -= attackamt - arcade_action() - - else if ("heal" in href_list) - blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - computer.updateUsrDialog() - turtle++ - - sleep(10) - player_mp -= pointamt - player_hp += healamt - blocked = 1 - computer.updateUsrDialog() - arcade_action() - - else if ("charge" in href_list) - blocked = 1 - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - player_mp += chargeamt - if(turtle > 0) - turtle-- - - computer.updateUsrDialog() - sleep(10) - arcade_action() - - if ("newgame" in href_list) //Reset everything - temp = "New Round" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - turtle = 0 - computer.updateUsrDialog() - - -/datum/file/program/arcade/proc/arcade_action() - if ((enemy_mp <= 0) || (enemy_hp <= 0)) - if(!gameover) - gameover = 1 - temp = "[enemy_name] has fallen! Rejoice!" - if(computer.toybox) - computer.toybox.dispense() - - else if ((enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - temp = "[enemy_name] steals [stealamt] of your power!" - player_mp -= stealamt - - if (player_mp <= 0) - gameover = 1 - sleep(10) - temp = "You have been drained! GAME OVER" - feedback_inc("arcade_loss_mana_normal") - - else if ((enemy_hp <= 10) && (enemy_mp > 4)) - temp = "[enemy_name] heals for 4 health!" - enemy_hp += 4 - enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - temp = "[enemy_name] attacks for [attackamt] damage!" - player_hp -= attackamt - - if ((player_mp <= 0) || (player_hp <= 0)) - gameover = 1 - temp = "You have been crushed! GAME OVER" - feedback_inc("arcade_loss_hp_normal") - - if(interactable()) - computer.updateUsrDialog() - blocked = 0 - return \ No newline at end of file diff --git a/code/game/machinery/computer3/computers/atmos_alert.dm b/code/game/machinery/computer3/computers/atmos_alert.dm deleted file mode 100644 index aaa677974c..0000000000 --- a/code/game/machinery/computer3/computers/atmos_alert.dm +++ /dev/null @@ -1,100 +0,0 @@ -/obj/machinery/computer3/atmos_alert - default_prog = /datum/file/program/atmos_alert - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio) - icon_state = "frame-eng" - -/datum/file/program/atmos_alert - name = "atmospheric alert monitor" - desc = "Recieves alerts over the radio." - active_state = "alert:2" - refresh = 1 - -/datum/file/program/atmos_alert/execute(var/datum/file/program/source) - ..(source) - - if(!computer.radio) - computer.Crash(MISSING_PERIPHERAL) - - computer.radio.set_frequency(1437,RADIO_ATMOSIA) - -// This will be called as long as the program is running on the parent computer -// and the computer has the radio peripheral -/datum/file/program/atmos_alert/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) - return - - var/zone = signal.data["zone"] - var/severity = signal.data["alert"] - if(!zone || !severity) - return - - minor_air_alarms -= zone - priority_air_alarms -= zone - if(severity=="severe") - priority_air_alarms += zone - else if (severity=="minor") - minor_air_alarms += zone - update_icon() - return - - -/datum/file/program/atmos_alert/interact() - if(!interactable()) - return - if(!computer.radio) - computer.Crash(MISSING_PERIPHERAL) - - popup.set_content(return_text()) - popup.open() - -/datum/file/program/atmos_alert/update_icon() - ..() - if(priority_air_alarms.len > 0) - overlay.icon_state = "alert:2" - else if(minor_air_alarms.len > 0) - overlay.icon_state = "alert:1" - else - overlay.icon_state = "alert:0" - - if(computer) - computer.update_icon() - - -/datum/file/program/atmos_alert/proc/return_text() - var/priority_text = "

    Priority Alerts:

    " - var/minor_text = "

    Minor Alerts:

    " - - if(priority_air_alarms.len) - for(var/zone in priority_air_alarms) - priority_text += "[format_text(zone)] [topic_link(src,"priority_clear=[ckey(zone)]","X")]
    " - else - priority_text += "No priority alerts detected.
    " - - if(minor_air_alarms.len) - for(var/zone in minor_air_alarms) - minor_text += "[format_text(zone)] [topic_link(src,"minor_clear=[ckey(zone)]","X")]
    " - else - minor_text += "No minor alerts detected.
    " - - return "[priority_text]

    [minor_text]
    [topic_link(src,"close","Close")]" - - -/datum/file/program/atmos_alert/Topic(var/href, var/list/href_list) - if(!interactable() || ..(href,href_list)) - return - - if("priority_clear" in href_list) - var/removing_zone = href_list["priority_clear"] - for(var/zone in priority_air_alarms) - if(ckey(zone) == removing_zone) - to_chat(usr, "Priority Alert for area [zone] cleared.") - priority_air_alarms -= zone - - if("minor_clear" in href_list) - var/removing_zone = href_list["minor_clear"] - for(var/zone in minor_air_alarms) - if(ckey(zone) == removing_zone) - to_chat(usr, "Minor Alert for area [zone] cleared.") - minor_air_alarms -= zone - - computer.updateUsrDialog() diff --git a/code/game/machinery/computer3/computers/camera.dm b/code/game/machinery/computer3/computers/camera.dm deleted file mode 100644 index 0017a83531..0000000000 --- a/code/game/machinery/computer3/computers/camera.dm +++ /dev/null @@ -1,352 +0,0 @@ -/* - Camera monitoring computers - - NOTE: If we actually split the station camera network into regions that will help with sorting through the - tediously large list of cameras. The new camnet_key architecture lets you switch between keys easily, - so you don't lose the capability of seeing everything, you just switch to a subnet. -*/ - -/obj/machinery/computer3/security - default_prog = /datum/file/program/security - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/cameras) - spawn_files = list(/datum/file/camnet_key) - icon_state = "frame-sec" - - -/obj/machinery/computer3/security/wooden_tv - name = "security cameras" - desc = "An old TV hooked into the stations camera network." - icon = 'icons/obj/computer.dmi' - icon_state = "security_det" - - legacy_icon = 1 - allow_disassemble = 0 - - // No operating system -/obj/machinery/computer3/security/wooden_tv/New() - ..(built=0) - os = program - circuitb.OS = os - - -/obj/machinery/computer3/security/mining - name = "Outpost Cameras" - desc = "Used to access the various cameras on the outpost." - spawn_files = list(/datum/file/camnet_key/mining) - -/* - Camera monitoring computers, wall-mounted -*/ -/obj/machinery/computer3/wall_comp/telescreen - default_prog = /datum/file/program/security - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/cameras) - spawn_files = list(/datum/file/camnet_key) - -/obj/machinery/computer3/wall_comp/telescreen/entertainment - desc = "Damn, they better have /tg/thechannel on these things." - spawn_files = list(/datum/file/camnet_key/entertainment) - - -/* - File containing an encrypted camera network key. - - (Where by encrypted I don't actually mean encrypted at all) -*/ -/datum/file/camnet_key - name = "Security Camera Network Main Key" - var/title = "Station" - var/desc = "Connects to station security cameras." - var/networks = list("ALL") // A little workaround as it is not possible to place station_networks here - var/screen = "cameras" - -/datum/file/camnet_key/execute(var/datum/file/source) - if(istype(source,/datum/file/program/security)) - var/datum/file/program/security/prog = source - prog.key = src - prog.camera_list = null - return - if(istype(source,/datum/file/program/ntos)) - for(var/obj/item/part/computer/storage/S in list(computer.hdd,computer.floppy)) - for(var/datum/file/F in S.files) - if(istype(F,/datum/file/program/security)) - var/datum/file/program/security/Sec = F - Sec.key = src - Sec.camera_list = null - Sec.execute(source) - return - computer.Crash(MISSING_PROGRAM) - -/datum/file/camnet_key/New() - for(var/N in networks) - if(N == "ALL") - networks = using_map.station_networks - break - return ..() - -/datum/file/camnet_key/mining - name = "Mining Camera Network Key" - title = "mining station" - desc = "Connects to mining security cameras." - networks = list(NETWORK_MINE) - screen = "miningcameras" - -/datum/file/camnet_key/research - name = "Research Camera Network Key" - title = "research" - networks = list(NETWORK_RESEARCH) - -/datum/file/camnet_key/bombrange - name = "R&D Bomb Range Camera Network Key" - title = "bomb range" - desc = "Monitors the bomb range." - networks = list(NETWORK_RESEARCH) - -/datum/file/camnet_key/xeno - name = "R&D Misc. Research Camera Network Key" - title = "special research" - networks = list(NETWORK_RESEARCH) - -/datum/file/camnet_key/singulo - name = "Singularity Camera Network Key" - title = "singularity" - networks = list(NETWORK_ENGINE) - -/datum/file/camnet_key/entertainment - name = "Entertainment Channel Encryption Key" - title = "entertainment" - desc = "Damn, I hope they have /tg/thechannel on here." - networks = list(NETWORK_THUNDER) - screen = "entertainment" - -/datum/file/camnet_key/creed - name = "Special Ops Camera Encryption Key" - title = "special ops" - desc = "Connects to special ops secure camera feeds." - networks = list(NETWORK_ERT) - -/datum/file/camnet_key/prison - name = "Prison Camera Network Key" - title = "prison" - desc = "Monitors the prison." - networks = list(NETWORK_SECURITY) - -/datum/file/camnet_key/syndicate - name = "Camera Network Key" - title = "%!#BUFFER OVERFLOW" - desc = "Connects to security cameras." - networks = list("ALL") - hidden_file = 1 - - -/* - Computer part needed to connect to cameras -*/ - -/obj/item/part/computer/networking/cameras - name = "camera network access module" - desc = "Connects a computer to the camera network." - - // I have no idea what the following does - var/mapping = 0//For the overview file, interesting bit of code. - - //proc/camera_list(var/datum/file/camnet_key/key) -/obj/item/part/computer/networking/cameras/get_machines(var/datum/file/camnet_key/key) - if (!computer || computer.z > 6) - return null - - cameranet.process_sort() - - var/list/L = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - var/list/temp = C.network & key.networks - if(temp.len) - L.Add(C) - - return L - -/obj/item/part/computer/networking/cameras/verify_machine(var/obj/machinery/camera/C,var/datum/file/camnet_key/key = null) - if(!istype(C) || !C.can_use()) - return 0 - - if(key) - var/list/temp = C.network & key.networks - if(!temp.len) - return 0 - return 1 - -/* - Camera monitoring program - - The following things should break you out of the camera view: - * The computer resetting, being damaged, losing power, etc - * The program quitting - * Closing the window - * Going out of range of the computer - * Becoming incapacitated - * The camera breaking, emping, disconnecting, etc -*/ - -/datum/file/program/security - name = "camera monitor" - desc = "Connects to the station camera network." - image = 'icons/ntos/camera.png' - active_state = "camera-static" - - var/datum/file/camnet_key/key = null - var/last_pic = 1.0 - var/last_camera_refresh = 0 - var/camera_list = null - - var/obj/machinery/camera/current = null - -/datum/file/program/security/execute(var/datum/file/program/caller) - ..(caller) - if(computer && !key) - var/list/fkeys = computer.list_files(/datum/file/camnet_key) - if(fkeys && fkeys.len) - key = fkeys[1] - update_icon() - computer.update_icon() - for(var/mob/living/L in viewers(1)) - if(!istype(L,/mob/living/silicon/ai) && L.machine == src) - L.reset_view(null) - - -/datum/file/program/security/Reset() - ..() - reset_current() - for(var/mob/living/L in viewers(1)) - if(!istype(L,/mob/living/silicon/ai) && L.machine == src) - L.reset_view(null) - -/datum/file/program/security/interact() - if(!interactable()) - return - - if(!computer.camnet) - computer.Crash(MISSING_PERIPHERAL) - return - - if(!key) - var/list/fkeys = computer.list_files(/datum/file/camnet_key) - if(fkeys && fkeys.len) - key = fkeys[1] - update_icon() - computer.update_icon() - if(!key) - return - - if(computer.camnet.verify_machine(current)) - usr.reset_view(current) - - if(world.time - last_camera_refresh > 50 || !camera_list) - last_camera_refresh = world.time - - var/list/temp_list = computer.camnet.get_machines(key) - - camera_list = "Network Key: [key.title] [topic_link(src,"keyselect","\[ Select key \]")]
    " - for(var/obj/machinery/camera/C in temp_list) - if(C.can_use()) - camera_list += "[C.c_tag] - [topic_link(src,"show=\ref[C]","Show")]
    " - else - camera_list += "[C.c_tag] - DEACTIVATED
    " - //camera_list += "
    " + topic_link(src,"close","Close") - - popup.set_content(camera_list) - popup.open() - - -/datum/file/program/security/update_icon() - if(key) - overlay.icon_state = key.screen - name = key.title + " Camera Monitor" - else - overlay.icon_state = "camera-static" - name = initial(name) - - - -/datum/file/program/security/Topic(var/href,var/list/href_list) - if(!interactable() || !computer.camnet || ..(href,href_list)) - return - - if("show" in href_list) - var/obj/machinery/camera/C = locate(href_list["show"]) - if(istype(C) && C.can_use()) - set_current(C) - usr.reset_view(C) - interact() - return - - if("keyselect" in href_list) - reset_current() - usr.reset_view(null) - key = input(usr,"Select a camera network key:", "Key Select", null) as null|anything in computer.list_files(/datum/file/camnet_key) - select_key(key) - if(key) - interact() - else - to_chat(usr, "The screen turns to static.") - return - -/datum/file/program/security/proc/select_key(var/selected_key) - key = selected_key - camera_list = null - update_icon() - computer.update_icon() - -/datum/file/program/security/proc/set_current(var/obj/machinery/camera/C) - if(current == C) - return - - if(current) - reset_current() - - src.current = C - if(current) - var/mob/living/L = current.loc - if(istype(L)) - L.tracking_initiated() - -/datum/file/program/security/proc/reset_current() - if(current) - var/mob/living/L = current.loc - if(istype(L)) - L.tracking_cancelled() - current = null - - // Atlantis: Required for camnetkeys to work. -/datum/file/program/security/hidden - hidden_file = 1 - -/* - Camera monitoring program - - Works much as the parent program, except: - * It requires a camera to be found using the proximity network card. - * It begins with all cam-access. -*/ - -/datum/file/program/security/syndicate - name = "camer# moni!%r" - desc = "Cons the Nanotrash Camera Network" - var/special_key = new/datum/file/camnet_key/syndicate - var/camera_conn = null - -/datum/file/program/security/syndicate/interact() - if(!interactable()) - return - - if(!computer.net) - computer.Crash(MISSING_PERIPHERAL) - return - - camera_conn = computer.net.connect_to(/obj/machinery/camera,camera_conn) - - if(!camera_conn) - computer.Crash(NETWORK_FAILURE) - return - - // On interact, override camera key selection - select_key(special_key) - ..() diff --git a/code/game/machinery/computer3/computers/communications.dm b/code/game/machinery/computer3/computers/communications.dm deleted file mode 100644 index de4f9c3dc1..0000000000 --- a/code/game/machinery/computer3/computers/communications.dm +++ /dev/null @@ -1,422 +0,0 @@ -/obj/machinery/computer3/communications - default_prog = /datum/file/program/communications - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio/subspace) - -/obj/machinery/computer3/communications/captain - default_prog = /datum/file/program/communications - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio/subspace,/obj/item/part/computer/cardslot/dual) - spawn_files = list(/datum/file/program/card_comp, /datum/file/program/security, /datum/file/program/crew, /datum/file/program/arcade, - /datum/file/camnet_key, /datum/file/camnet_key/entertainment, /datum/file/camnet_key/singulo) - - -/datum/file/program/communications - var/const/STATE_DEFAULT = 1 - var/const/STATE_CALLSHUTTLE = 2 - var/const/STATE_CANCELSHUTTLE = 3 - var/const/STATE_MESSAGELIST = 4 - var/const/STATE_VIEWMESSAGE = 5 - var/const/STATE_DELMESSAGE = 6 - var/const/STATE_STATUSDISPLAY = 7 - var/const/STATE_ALERT_LEVEL = 8 - var/const/STATE_CONFIRM_LEVEL = 9 - - -/datum/file/program/communications - name = "CentCom communications relay" - desc = "Used to connect to CentCom." - active_state = "comm" - req_access = list(access_heads) - - var/prints_intercept = 1 - var/authenticated = 0 - var/list/messagetitle = list() - var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 - var/state = STATE_DEFAULT - var/aistate = STATE_DEFAULT - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 - var/tmp_alertlevel = 0 - - var/status_display_freq = "1435" - var/stat_msg1 - var/stat_msg2 - - var/datum/announcement/priority/crew_announcement = new - -/datum/file/program/communications/New() - ..() - crew_announcement.newscast = 1 - -/datum/file/program/communications/Reset() - ..() - authenticated = 0 - state = STATE_DEFAULT - aistate = STATE_DEFAULT - - -/datum/file/program/communications/Topic(var/href, var/list/href_list) - if(!interactable() || !computer.radio || ..(href,href_list) ) - return - if (computer.z > 1) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return - - if("main" in href_list) - state = STATE_DEFAULT - - if("login" in href_list) - var/mob/M = usr - var/obj/item/I = M.get_active_hand() - if(I) - I = I.GetID() - if(istype(I,/obj/item/weapon/card/id) && check_access(I)) - authenticated = 1 - if(access_captain in I.GetAccess()) - authenticated = 2 - crew_announcement.announcer = GetNameAndAssignmentFromId(I) - if(istype(I,/obj/item/weapon/card/emag)) - authenticated = 2 - computer.emagged = 1 - - if("logout" in href_list) - authenticated = 0 - crew_announcement.announcer = "" - - if("swipeidseclevel" in href_list) - var/mob/M = usr - var/obj/item/I = M.get_active_hand() - I = I.GetID() - - if (istype(I,/obj/item/weapon/card/id)) - if(access_captain in I.GetAccess()) - var/old_level = security_level - if(!tmp_alertlevel) - tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) - tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) - tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) - if(security_level != old_level) - //Only notify the admins if an actual change happened - log_game("[key_name(usr)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - switch(security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green",1) - if(SEC_LEVEL_YELLOW) - feedback_inc("alert_comms_yellow",1) - if(SEC_LEVEL_VIOLET) - feedback_inc("alert_comms_violet",1) - if(SEC_LEVEL_ORANGE) - feedback_inc("alert_comms_orange",1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue",1) - tmp_alertlevel = 0 - else - to_chat(usr, "You are not authorized to do this.") - tmp_alertlevel = 0 - state = STATE_DEFAULT - else - to_chat(usr, "You need to swipe your ID.") - - if("announce" in href_list) - if(authenticated==2) - if(message_cooldown) - usr << "Please allow at least one minute to pass between announcements" - return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") - if(!input || !interactable()) - return - crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - - if("callshuttle" in href_list) - state = STATE_DEFAULT - if(authenticated) - state = STATE_CALLSHUTTLE - - if("callshuttle2" in href_list) - if(!computer.radio.subspace) - return - if(authenticated) - call_shuttle_proc(usr) - if(emergency_shuttle.online()) - post_status("shuttle") - state = STATE_DEFAULT - - if("cancelshuttle" in href_list) - state = STATE_DEFAULT - if(authenticated) - state = STATE_CANCELSHUTTLE - - if("messagelist" in href_list) - currmsg = 0 - state = STATE_MESSAGELIST - - if("viewmessage" in href_list) - state = STATE_VIEWMESSAGE - if (!currmsg) - if(href_list["message-num"]) - currmsg = text2num(href_list["message-num"]) - else - state = STATE_MESSAGELIST - - if("delmessage" in href_list) - state = (currmsg) ? STATE_DELMESSAGE : STATE_MESSAGELIST - - if("delmessage2" in href_list) - if(authenticated) - if(currmsg) - var/title = messagetitle[currmsg] - var/text = messagetext[currmsg] - messagetitle.Remove(title) - messagetext.Remove(text) - if(currmsg == aicurrmsg) - aicurrmsg = 0 - currmsg = 0 - state = STATE_MESSAGELIST - else - state = STATE_VIEWMESSAGE - - if("status" in href_list) - state = STATE_STATUSDISPLAY - - // Status display stuff - if("setstat" in href_list) - switch(href_list["statdisp"]) - if("message") - post_status("message", stat_msg1, stat_msg2) - if("alert") - post_status("alert", href_list["alert"]) - else - post_status(href_list["statdisp"]) - - if("setmsg1" in href_list) - stat_msg1 = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", stat_msg1) as text|null, 40), 40) - computer.updateDialog() - - if("setmsg2" in href_list) - stat_msg2 = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", stat_msg2) as text|null, 40), 40) - computer.updateDialog() - - // OMG CENTCOM LETTERHEAD - if("MessageCentCom" in href_list) - if(!computer.radio.subspace) - return - if(authenticated==2) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - return - var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "")) - if(!input || !interactable()) - return - CentCom_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a [using_map.boss_short] announcement: [input]") - centcomm_message_cooldown = 1 - spawn(600)//10 minute cooldown - centcomm_message_cooldown = 0 - - // OMG SYNDICATE ...LETTERHEAD - if("MessageSyndicate" in href_list) - if((authenticated==2) && (computer.emagged)) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - return - var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "")) - if(!input || !interactable()) - return - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made an illegal announcement: [input]") - centcomm_message_cooldown = 1 - spawn(600)//10 minute cooldown - centcomm_message_cooldown = 0 - - if("RestoreBackup" in href_list) - to_chat(usr, "Backup routing data restored!") - computer.emagged = 0 - computer.updateDialog() - - - // AI interface - if("ai-main" in href_list) - aicurrmsg = 0 - aistate = STATE_DEFAULT - - if("ai-callshuttle" in href_list) - aistate = STATE_CALLSHUTTLE - - if("ai-callshuttle2" in href_list) - if(!computer.radio.subspace) - return - call_shuttle_proc(usr) - aistate = STATE_DEFAULT - - if("ai-messagelist" in href_list) - aicurrmsg = 0 - aistate = STATE_MESSAGELIST - - if("ai-viewmessage" in href_list) - aistate = STATE_VIEWMESSAGE - if (!aicurrmsg) - if(href_list["message-num"]) - aicurrmsg = text2num(href_list["message-num"]) - else - aistate = STATE_MESSAGELIST - - if("ai-delmessage" in href_list) - aistate = (aicurrmsg) ? STATE_DELMESSAGE : STATE_MESSAGELIST - - if("ai-delmessage2" in href_list) - if(aicurrmsg) - var/title = messagetitle[aicurrmsg] - var/text = messagetext[aicurrmsg] - messagetitle.Remove(title) - messagetext.Remove(text) - if(currmsg == aicurrmsg) - currmsg = 0 - aicurrmsg = 0 - aistate = STATE_MESSAGELIST - - if("ai-status" in href_list) - aistate = STATE_STATUSDISPLAY - - if("securitylevel" in href_list) - tmp_alertlevel = text2num( href_list["newalertlevel"] ) - if(!tmp_alertlevel) tmp_alertlevel = 0 - state = STATE_CONFIRM_LEVEL - - if("changeseclevel" in href_list) - state = STATE_ALERT_LEVEL - - computer.updateUsrDialog() - - - -/datum/file/program/communications/proc/main_menu() - var/dat = "" - if (computer.radio.subspace) - if(emergency_shuttle.online() && emergency_shuttle.location()) - var/timeleft = emergency_shuttle.estimate_arrival_time() - dat += "Emergency shuttle\n
    \nETA: [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " - refresh = 1 - else - refresh = 0 - if (authenticated) - dat += "
    \[ Log Out \]" - if (authenticated==2) - dat += "
    \[ Make An Announcement \]" - if(computer.emagged == 0) - dat += "
    \[ Send an emergency message to [using_map.boss_short] \]" - else - dat += "
    \[ Send an emergency message to \[UNKNOWN\] \]" - dat += "
    \[ Restore Backup Routing Data \]" - - dat += "
    \[ Change alert level \]" - if(emergency_shuttle.location()) - if (emergency_shuttle.online()) - dat += "
    \[ Cancel Shuttle Call \]" - else - dat += "
    \[ Call Emergency Shuttle \]" - - dat += "
    \[ Set Status Display \]" - else - dat += "
    \[ Log In \]" - dat += "
    \[ Message List \]" - return dat - -/datum/file/program/communications/proc/confirm_menu(var/prompt,var/yes_option) - return "Are you sure you want to [prompt]? \[ [topic_link(src,yes_option,"OK")] | [topic_link(src,"main","Cancel")] \]" - -/datum/file/program/communications/interact() - if(!interactable()) - return - if(!computer.radio) - computer.Crash(MISSING_PERIPHERAL) - return - - var/dat = "" - switch(state) - if(STATE_DEFAULT) - dat = main_menu() - if(STATE_CALLSHUTTLE) - dat = confirm_menu("call the shuttle","callshuttle2") - if(STATE_CANCELSHUTTLE) - dat = confirm_menu("cancel the shuttle","cancelshuttle2") - if(STATE_MESSAGELIST) - dat += "Messages:" - for(var/i = 1; i<=messagetitle.len; i++) - dat += "
    [messagetitle[i]]" - if(STATE_VIEWMESSAGE) - if (currmsg) - dat += "[messagetitle[currmsg]]

    [messagetext[currmsg]]" - if (authenticated) - dat += "

    \[ Delete \]" - else - state = STATE_MESSAGELIST - interact() - return - if(STATE_DELMESSAGE) - if (currmsg) - dat += "Are you sure you want to delete this message? \[ OK | Cancel \]" - else - state = STATE_MESSAGELIST - interact() - return - if(STATE_STATUSDISPLAY) - dat += "\[ Back \]
    " - dat += "Set Status Displays
    " - dat += "\[ Clear \]
    " - dat += "\[ Station Time \]" - dat += "\[ Shuttle ETA \]
    " - dat += "\[ Message \]" - dat += "
    " - dat += "\[ Alert: None |" - dat += " Red Alert |" - dat += " Lockdown |" - dat += " Biohazard \]

    " - if(STATE_ALERT_LEVEL) - dat += "Current alert level: [get_security_level()]
    " - if(security_level == SEC_LEVEL_DELTA) - dat += "The self-destruct mechanism is active. Find a way to deactivate the mechanism to lower the alert level or evacuate." - else - dat += "Blue
    " - dat += "Orange
    " - dat += "Violet
    " - dat += "Yellow
    " - dat += "Green" - if(STATE_CONFIRM_LEVEL) - dat += "Current alert level: [get_security_level()]
    " - dat += "Confirm the change to: [num2seclevel(tmp_alertlevel)]
    " - dat += "Swipe ID to confirm change.
    " - - popup.set_content(dat) - popup.open() - - -/datum/file/program/communications/proc/post_status(var/command, var/data1, var/data2) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) - - if(!frequency) - return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = 1 - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - if("alert") - status_signal.data["picture_state"] = data1 - - frequency.post_signal(src, status_signal) diff --git a/code/game/machinery/computer3/computers/crew.dm b/code/game/machinery/computer3/computers/crew.dm deleted file mode 100644 index e0112532dd..0000000000 --- a/code/game/machinery/computer3/computers/crew.dm +++ /dev/null @@ -1,76 +0,0 @@ -/obj/machinery/computer3/crew - default_prog = /datum/file/program/crew - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio) - icon_state = "frame-med" - -/datum/file/program/crew - name = "Crew Monitoring Console" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - active_state = "crew" - var/list/tracked = list( ) - -/datum/file/program/crew/interact(mob/user) - if(!interactable()) - return - - scan() - var/t = "Crew Monitoring
    " - t += "
    Refresh " - t += "Close
    " - t += "" - var/list/logs = list() - for(var/obj/item/clothing/under/C in src.tracked) - var/log = "" - var/turf/pos = get_turf(C) - if((C) && (C.has_sensor) && (pos) && (pos.z == computer.z) && C.sensor_mode) - if(istype(C.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C.loc - - var/dam1 = round(H.getOxyLoss(),1) - var/dam2 = round(H.getToxLoss(),1) - var/dam3 = round(H.getFireLoss(),1) - var/dam4 = round(H.getBruteLoss(),1) - - var/life_status = "[H.stat > 1 ? "Deceased" : "Living"]" - var/damage_report = "([dam1]/[dam2]/[dam3]/[dam4])" - - log += "" - - switch(C.sensor_mode) - if(1) - log += "" - if(2) - log += "" - if(3) - var/area/player_area = get_area(H) - log += "" - logs += log - logs = sortList(logs) - for(var/log in logs) - t += log - t += "
    NameVitalsPosition
    [H.get_authentification_name()] ([H.get_assignment()])[life_status]Not Available
    [life_status] [damage_report]Not Available
    [life_status] [damage_report][sanitize(player_area.name)] ([pos.x], [pos.y])
    " - t += "
    " - - popup.set_content(t) - popup.open() - - -/datum/file/program/crew/proc/scan() - for(var/obj/item/clothing/under/C in world) - if((C.has_sensor) && (istype(C.loc, /mob/living/carbon/human))) - tracked |= C - return 1 - -/datum/file/program/crew/Topic(href, list/href_list) - if(!interactable() || !computer.cardslot || ..(href,href_list)) - return - - if( href_list["close"] ) - usr << browse(null, "window=crewcomp") - usr.unset_machine() - return - - if(href_list["update"]) - interact() - //src.updateUsrDialog() - return diff --git a/code/game/machinery/computer3/computers/customs.dm b/code/game/machinery/computer3/computers/customs.dm deleted file mode 100644 index bca73055b8..0000000000 --- a/code/game/machinery/computer3/computers/customs.dm +++ /dev/null @@ -1,3 +0,0 @@ -/obj/machinery/computer3/customs - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio/subspace,/obj/item/part/computer/networking/cameras) - spawn_files = list(/datum/file/program/arcade,/datum/file/program/security,/datum/file/camnet_key/entertainment,/datum/file/program/crew) \ No newline at end of file diff --git a/code/game/machinery/computer3/computers/law.dm b/code/game/machinery/computer3/computers/law.dm deleted file mode 100644 index e79b09c9e9..0000000000 --- a/code/game/machinery/computer3/computers/law.dm +++ /dev/null @@ -1,84 +0,0 @@ - - -/obj/machinery/computer3/aiupload - name = "AI Upload" - desc = "Used to upload laws to the AI." - icon_state = "frame-rnd" - circuit = /obj/item/weapon/circuitboard/aiupload - var/mob/living/silicon/ai/current = null - var/opened = 0 - - -/obj/machinery/computer3/aiupload/verb/AccessInternals() - set category = "Object" - set name = "Access Computer's Internals" - set src in oview(1) - if(!Adjacent(usr) || usr.restrained() || usr.lying || usr.stat || istype(usr, /mob/living/silicon) || !istype(usr, /mob/living)) - return - - opened = !opened - if(opened) - to_chat(usr, "The access panel is now open.") - else - to_chat(usr, "The access panel is now closed.") - return - - -/obj/machinery/computer3/aiupload/attackby(obj/item/weapon/aiModule/module as obj, mob/user as mob) - if (user.z > 6) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - if(istype(module, /obj/item/weapon/aiModule)) - module.install(src, user) - else - return ..() - - -/obj/machinery/computer3/aiupload/attack_hand(var/mob/user as mob) - if(src.stat & NOPOWER) - to_chat(user, "The upload computer has no power!") - return - if(src.stat & BROKEN) - to_chat(user, "The upload computer is broken!") - return - - src.current = select_active_ai(user) - - if (!src.current) - to_chat(user, "No active AIs detected.") - else - to_chat(user, "[src.current.name] selected for law changes.") - return - - - -/obj/machinery/computer3/borgupload - name = "Cyborg Upload" - desc = "Used to upload laws to Cyborgs." - icon_state = "frame-rnd" - circuit = /obj/item/weapon/circuitboard/borgupload - var/mob/living/silicon/robot/current = null - - -/obj/machinery/computer3/borgupload/attackby(obj/item/weapon/aiModule/module as obj, mob/user as mob) - if(istype(module, /obj/item/weapon/aiModule)) - module.install(src, user) - else - return ..() - - -/obj/machinery/computer3/borgupload/attack_hand(var/mob/user as mob) - if(src.stat & NOPOWER) - to_chat(user, "The upload computer has no power!") - return - if(src.stat & BROKEN) - to_chat(user, "The upload computer is broken!") - return - - src.current = freeborg() - - if (!src.current) - to_chat(user, "No free cyborgs detected.") - else - to_chat(user, "[src.current.name] selected for law changes.") - return diff --git a/code/game/machinery/computer3/computers/medical.dm b/code/game/machinery/computer3/computers/medical.dm deleted file mode 100644 index d1f20c7363..0000000000 --- a/code/game/machinery/computer3/computers/medical.dm +++ /dev/null @@ -1,512 +0,0 @@ -/* - I hate to make this a todo, but I cannot possibly complete all of computer3 - if I have to rearchitecture datacores and everything else that uses them right now. - - In the future the datacore should probably be a server, perhaps on station, perhaps on centcom, - with data records as files probably. It's not difficult unless you're trying to do a million - impossible things before breakfast. -*/ - -/obj/machinery/computer3/med_data - default_prog = /datum/file/program/med_data - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/cardslot,/obj/item/part/computer/networking/radio) - - -/obj/machinery/computer3/laptop/medical - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/cardslot,/obj/item/part/computer/networking/radio) - spawn_files = list(/datum/file/program/arcade,/datum/file/program/crew,/datum/file/program/med_data) - -/datum/file/program/med_data - name = "Medical Records" - desc = "This can be used to check medical records." - active_state = "medcomp" - req_one_access = list(access_medical, access_forensics_lockers) - - var/obj/item/weapon/card/id/scan = null - var/obj/item/weapon/card/id/scan2 = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/a_id = null - var/temp = null - var/printing = null - - -/datum/file/program/med_data/proc/authenticate() - if(isAI(usr) || access_medical in scan.access) - return 1 - return 0 - -/datum/file/program/med_data/interact() - if(!computer.cardslot) - computer.Crash(MISSING_PERIPHERAL) - return - usr.set_machine(src) - scan = computer.cardslot.reader - if(!interactable()) - return - if(computer.z > 6) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return - var/dat - - if (temp) - dat = text("[src.temp]

    Clear Screen") - else - dat = text("Confirm Identity (R): []
    ", src, (scan ? text("[]", scan.name) : "----------")) - if (istype(computer.cardslot, /obj/item/part/computer/cardslot/dual)) - dat += text("Check Identity (W): []
    ", src, (scan2 ? text("[]", scan2.name) : "----------")) - if(scan2 && !scan) - dat += text("
    Insert card into reader slot to log in.

    ") - - if (src.authenticated) - switch(src.screen) - if(1.0) - dat += {" - Search Records -
    List Records -
    -
    Virus Database -
    Medbot Tracking -
    -
    Record Maintenance -
    {Log Out}
    - "} - if(2.0) - dat += "Record List:
    " - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general)) - dat += text("[]: []
    ", src, R, R.fields["id"], R.fields["name"]) - //Foreach goto(132) - dat += text("
    Back", src) - if(3.0) - dat += text("Records Maintenance
    \nBackup To Disk
    \nUpload From disk
    \nDelete All Records
    \n
    \nBack", src, src, src, src) - if(4.0) - dat += "
    Medical Record

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

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

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

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

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

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

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

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

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

    ", record2.fields["b_type"], record2.fields["b_dna"], record2.fields["mi_dis"], record2.fields["mi_dis_d"], record2.fields["ma_dis"], record2.fields["ma_dis_d"], record2.fields["alg"], record2.fields["alg_d"], record2.fields["cdi"], record2.fields["cdi_d"], decode(record2.fields["notes"])) - var/counter = 1 - while(record2.fields[text("com_[]", counter)]) - P.info += text("[]
    ", record2.fields[text("com_[]", counter)]) - counter++ - else - P.info += "Medical Record Lost!
    " - P.info += "" - src.printing = null - interact() - return diff --git a/code/game/machinery/computer3/computers/power.dm b/code/game/machinery/computer3/computers/power.dm deleted file mode 100644 index b6830a4cd3..0000000000 --- a/code/game/machinery/computer3/computers/power.dm +++ /dev/null @@ -1,48 +0,0 @@ -/obj/machinery/computer3/powermonitor - default_prog = /datum/file/program/powermon - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/cable) - icon_state = "frame-eng" - -/datum/file/program/powermon - name = "power monitoring console" - desc = "It monitors APC status." - active_state = "power" - -/datum/file/program/powermon/proc/format(var/obj/machinery/power/apc/A) - var/static/list/S = list(" Off","AOff"," On", " AOn") - var/static/list/chg = list("N","C","F") - return "[copytext(add_tspace("\The [A.area]", 30), 1, 30)] [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] \ - [add_lspace(A.lastused_total, 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]
    " - -/datum/file/program/powermon/interact() - if(!interactable()) - return - if(!computer.net) - computer.Crash(MISSING_PERIPHERAL) - return - var/list/L = computer.net.get_machines(/obj/machinery/power/apc) - var/t = "" - t += "Refresh

    " - if(!L || !L.len) - t += "No connection" - else - var/datum/powernet/powernet = computer.net.connect_to(/datum/powernet,null) - if(powernet) - t += "
    Total power: [powernet.avail] W
    Total load: [num2text(powernet.viewload,10)] W
    " - else - t += "
    Power statistics unavailable
    " - t += "" - - if(L.len > 0) - t += "Area Eqp./Lgt./Env. Load Cell
    " - for(var/obj/machinery/power/apc/A in L) - t += src.format(A) - t += "
    " - - popup.set_content(t) - popup.open() - -/datum/file/program/powermon/Topic(var/href, var/list/href_list) - if(!interactable() || ..(href,href_list)) - return - interact() diff --git a/code/game/machinery/computer3/computers/prisoner.dm b/code/game/machinery/computer3/computers/prisoner.dm deleted file mode 100644 index b702c25ba2..0000000000 --- a/code/game/machinery/computer3/computers/prisoner.dm +++ /dev/null @@ -1,106 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -/obj/machinery/computer3/prisoner - default_prog = /datum/file/program/prisoner - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio) - icon_state = "frame-sec" - -/datum/file/program/prisoner - name = "Prisoner Management Console" - active_state = "explosive" - req_access = list(access_armory) - - var/id = 0.0 - var/temp = null - var/status = 0 - var/timeleft = 60 - var/stop = 0.0 - var/screen = 0 // 0 - No Access Denied, 1 - Access allowed - - -/datum/file/program/prisoner/interact() - if(!interactable()) - return - var/dat - dat += "Prisoner Implant Manager System
    " - if(screen == 0) - dat += "
    Unlock Console" - else if(screen == 1) - dat += "
    Chemical Implants
    " - var/turf/Tr = null - for(var/obj/item/weapon/implant/chem/C in all_chem_implants) - Tr = get_turf(C) - if((Tr) && (Tr.z != computer.z)) - continue //Out of range - if(!C.implanted) - continue - dat += "[C.imp_in.name] | Remaining Units: [C.reagents.total_volume] | Inject: " - dat += "((1))" - dat += "((5))" - dat += "((10))
    " - dat += "********************************
    " - dat += "
    Tracking Implants
    " - for(var/obj/item/weapon/implant/tracking/T in all_tracking_implants) - Tr = get_turf(T) - if((Tr) && (Tr.z != computer.z)) - continue //Out of range - if(!T.implanted) - continue - var/loc_display = "Unknown" - var/mob/living/carbon/M = T.imp_in - if(M.z in using_map.station_levels && !istype(M.loc, /turf/space)) - var/turf/mob_loc = get_turf(M) - loc_display = mob_loc.loc - if(T.malfunction) - loc_display = pick(teleportlocs) - dat += "ID: [T.id] | Location: [loc_display]
    " - dat += "(Send Message) |
    " - dat += "********************************
    " - dat += "
    Lock Console" - - popup.width = 400 - popup.height = 500 - popup.set_content(dat) - popup.set_title_image(usr.browse_rsc_icon(computer.icon, computer.icon_state)) - popup.open() - return - -/datum/file/program/prisoner/process() - if(!..()) - interact() - return - -/datum/file/program/prisoner/Topic(href, href_list) - if(!interactable() || ..(href,href_list)) - return - - if(href_list["inject1"]) - var/obj/item/weapon/implant/I = locate(href_list["inject1"]) - if(istype(I)) - I.activate(1) - - else if(href_list["inject5"]) - var/obj/item/weapon/implant/I = locate(href_list["inject5"]) - if(istype(I)) - I.activate(5) - - else if(href_list["inject10"]) - var/obj/item/weapon/implant/I = locate(href_list["inject10"]) - if(istype(I)) - I.activate(10) - - else if(href_list["lock"]) - screen = !screen - - else if(href_list["warn"]) - var/warning = sanitize(input(usr,"Message:","Enter your message here!","")) - if(!warning) return - var/obj/item/weapon/implant/I = locate(href_list["warn"]) - if(istype(I) && I.imp_in) - var/mob/living/carbon/R = I.imp_in - log_game("PrisonComputer3 message: [key_name(usr)]->[key_name(R)] : [warning]") - to_chat(R, "You hear a voice in your head saying: '[warning]'") - - interact() - return - - diff --git a/code/game/machinery/computer3/computers/robot.dm b/code/game/machinery/computer3/computers/robot.dm deleted file mode 100644 index 35c1090f67..0000000000 --- a/code/game/machinery/computer3/computers/robot.dm +++ /dev/null @@ -1,210 +0,0 @@ -/obj/machinery/computer3/robotics - default_prog = /datum/file/program/borg_control - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio) - icon_state = "frame-rnd" - -/datum/file/program/borg_control - name = "Cyborg Control" - desc = "Used to remotely lockdown or detonate linked Cyborgs." - active_state = "robot" - var/id = 0.0 - var/temp = null - var/status = 0 - var/timeleft = 60 - var/stop = 0.0 - var/screen = 0 // 0 - Main Menu, 1 - Cyborg Status, 2 - Kill 'em All! -- In text - req_access = list(access_robotics) - -/datum/file/program/borg_control/proc/start_sequence() - do - if(src.stop) - src.stop = 0 - return - src.timeleft-- - sleep(10) - while(src.timeleft) - - for(var/mob/living/silicon/robot/R in mob_list) - if(!R.scrambledcodes) - R.self_destruct() - return - - -/datum/file/program/borg_control/interact() - if(!interactable() || computer.z > 6) - return - var/dat - if (src.temp) - dat = "[src.temp]

    Clear Screen" - else - if(screen == 0) - //dat += "

    Cyborg Control Console


    " - dat += "1. Cyborg Status
    " - dat += "2. Emergency Full Destruct
    " - if(screen == 1) - for(var/mob/living/silicon/robot/R in mob_list) - if(istype(usr, /mob/living/silicon/ai)) - if (R.connected_ai != usr) - continue - if(istype(usr, /mob/living/silicon/robot)) - if (R != usr) - continue - if(R.scrambledcodes) - continue - - dat += "[R.name] |" - if(R.stat) - dat += " Not Responding |" - else if (!R.canmove) - dat += " Locked Down |" - else - dat += " Operating Normally |" - if (!R.canmove) - else if(R.cell) - dat += " Battery Installed ([R.cell.charge]/[R.cell.maxcharge]) |" - else - dat += " No Cell Installed |" - if(R.module) - dat += " Module Installed ([R.module.name]) |" - else - dat += " No Module Installed |" - if(R.connected_ai) - dat += " Slaved to [R.connected_ai.name] |" - else - dat += " Independent from AI |" - if (istype(usr, /mob/living/silicon)) - if(issilicon(usr) && is_special_character(usr) && !R.emagged) - dat += "(Hack) " - dat += "([R.canmove ? "Lockdown" : "Release"]) " - dat += "(Destroy)" - dat += "
    " - dat += "(Return to Main Menu)
    " - if(screen == 2) - if(!src.status) - dat += {"
    Emergency Robot Self-Destruct
    \nStatus: Off
    - \n
    - \nCountdown: [src.timeleft]/60 \[Reset\]
    - \n
    - \nStart Sequence
    - \n
    - \nClose"} - else - dat = {"Emergency Robot Self-Destruct
    \nStatus: Activated
    - \n
    - \nCountdown: [src.timeleft]/60 \[Reset\]
    - \n
    \nStop Sequence
    - \n
    - \nClose"} - dat += "(Return to Main Menu)
    " - popup.set_content(dat) - popup.open() - return - -/datum/file/program/borg_control/Topic(var/href, var/list/href_list) - if(!interactable() || ..(href,href_list)) - return - - if("killall" in href_list) - src.temp = {"Destroy Robots?
    -
    \[Swipe ID to initiate destruction sequence\]
    - Cancel"} - - if("do_killall" in href_list) - var/obj/item/weapon/card/id/I = usr.get_active_hand() - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/pda = I - I = pda.id - if(istype(I)) - if(src.check_access(I)) - if(!status) - message_admins("[key_name_admin(usr)] has initiated the global cyborg killswitch!") - log_game("[key_name(usr)] has initiated the global cyborg killswitch!") - src.status = 1 - src.start_sequence() - src.temp = null - - else - to_chat(usr, "Access Denied.") - - if("stop" in href_list) - src.temp = {" - Stop Robot Destruction Sequence?
    -
    Yes
    - No"} - - if("stop2" in href_list) - src.stop = 1 - src.temp = null - src.status = 0 - - if("reset" in href_list) - src.timeleft = 60 - - if("temp" in href_list) - src.temp = null - if("screen" in href_list) - switch(href_list["screen"]) - if("0") - screen = 0 - if("1") - screen = 1 - if("2") - screen = 2 - if("killbot" in href_list) - if(computer.allowed(usr)) - var/mob/living/silicon/robot/R = locate(href_list["killbot"]) - if(R) - var/choice = input("Are you certain you wish to detonate [R.name]?") in list("Confirm", "Abort") - if(choice == "Confirm") - if(R && istype(R)) - if(R.mind && R.mind.special_role && R.emagged) - to_chat(R, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - R.ResetSecurityCodes() - - else - message_admins("[key_name_admin(usr)] detonated [key_name(R.name)]!") - log_game("[key_name_admin(usr)] detonated [key_name(R.name)]!") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg kill-switch activated: [R.name]
    ") - R.self_destruct() - else - to_chat(usr, "Access Denied.") - - if("stopbot" in href_list) - if(computer.allowed(usr)) - var/mob/living/silicon/robot/R = locate(href_list["stopbot"]) - if(R && istype(R)) // Extra sancheck because of input var references - var/choice = input("Are you certain you wish to [R.canmove ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort") - if(choice == "Confirm") - if(R && istype(R)) - message_admins("[key_name_admin(usr)] [R.canmove ? "locked down" : "released"] [R.name]!") - log_game("[key_name(usr)] [R.canmove ? "locked down" : "released"] [key_name(R.name)]!") - R.canmove = !R.canmove - if(R.lockcharge) - R.lockcharge = !R.lockcharge - to_chat(R, "Your lockdown has been lifted!") - else - R.lockcharge = !R.lockcharge - to_chat(R, "You have been locked down!") - - else - to_chat(usr, "Access Denied.") - - if ("magbot" in href_list) - if(computer.allowed(usr)) - var/mob/living/silicon/robot/R = locate(href_list["magbot"]) - if(R) - var/choice = input("Are you certain you wish to hack [R.name]?") in list("Confirm", "Abort") - if(choice == "Confirm") - if(R && istype(R)) -// message_admins("[key_name_admin(usr)] emagged [R.name] using robotic console!") // why is this commented out? - log_game("[key_name(usr)] emagged [R.name] using robotic console!") - R.emagged = 1 - if(R.mind.special_role) - R.verbs += /mob/living/silicon/robot/proc/ResetSecurityCodes - - interact() - return - - - diff --git a/code/game/machinery/computer3/computers/security.dm b/code/game/machinery/computer3/computers/security.dm deleted file mode 100644 index 92b1469edd..0000000000 --- a/code/game/machinery/computer3/computers/security.dm +++ /dev/null @@ -1,613 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -/obj/machinery/computer3/secure_data - default_prog = /datum/file/program/secure_data - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/cardslot,/obj/item/part/computer/networking/radio) - icon_state = "frame-sec" - -/obj/machinery/computer3/laptop/secure_data - default_prog = /datum/file/program/secure_data - spawn_parts = list(/obj/item/part/computer/storage/hdd/big,/obj/item/part/computer/cardslot,/obj/item/part/computer/networking/radio) - icon_state = "laptop" - - -/datum/file/program/secure_data - name = "Security Records" - desc = "Used to view and edit personnel's security records" - active_state = "security" - image = 'icons/ntos/records.png' - - req_one_access = list(access_security, access_forensics_lockers) - - var/obj/item/weapon/card/id/scan = null - var/obj/item/weapon/card/id/scan2 = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/a_id = null - var/temp = null - var/printing = null - var/can_change_id = 0 - var/list/Perp - var/tempname = null - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending // 1 = Ascending - - - -/datum/file/program/secure_data/proc/authenticate() - if(access_security in scan.access || access_forensics_lockers in scan.access ) - return 1 - if(isAI(usr)) - return 1 - return 0 - -/datum/file/program/secure_data/interact() - if(!computer.cardslot) - computer.Crash(MISSING_PERIPHERAL) - return - usr.set_machine(src) - scan = computer.cardslot.reader - - if(istype(computer.cardslot, /obj/item/part/computer/cardslot/dual)) - var/obj/item/part/computer/cardslot/dual/D = computer.cardslot - scan2 = D.writer - - if(!interactable()) - return - - if(computer.z > 6) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return - var/dat - - if(temp) - dat = text("[]

    Clear Screen", temp, src) - else - dat = text("Confirm Identity (R): []
    ", src, (scan ? text("[]", scan.name) : "----------")) - if(istype(computer.cardslot, /obj/item/part/computer/cardslot/dual)) - dat += text("Confirm Identity (W): []
    ", src, (scan2 ? text("[]", scan2.name) : "----------")) - if(scan2 && !scan) - dat += text("
    Insert card into reader slot to log in.

    ") - if(authenticated) - switch(screen) - if(1.0) - dat += "

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

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

    " - dat += text("Record Maintenance

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

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

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/icon/front = active1.fields["photo_front"] - var/icon/side = active1.fields["photo_side"] - usr << browse_rsc(front, "front.png") - usr << browse_rsc(side, "side.png") - dat += text(" \ -
    \ - Name: [active1.fields["name"]]
    \ - ID: [active1.fields["id"]]
    \n \ - Entity Classification: [active1.fields["brain_type"]]
    \n \ - Sex: [active1.fields["sex"]]
    \n \ - Age: [active1.fields["age"]]
    \n \ - Rank: [active1.fields["rank"]]
    \n \ - Fingerprint: [active1.fields["fingerprint"]]
    \n \ - Physical Status: [active1.fields["p_stat"]]
    \n \ - Mental Status: [active1.fields["m_stat"]]
    Photo:
    \ -
    ") - else - dat += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - dat += text("
    \n
    Security Data

    \nCriminal Status: []
    \n
    \nMinor Crimes: []
    \nDetails: []
    \n
    \nMajor Crimes: []
    \nDetails: []
    \n
    \nImportant Notes:
    \n\t[]
    \n
    \n
    Comments/Log

    ", src, active2.fields["criminal"], src, active2.fields["mi_crim"], src, active2.fields["mi_crim_d"], src, active2.fields["ma_crim"], src, active2.fields["ma_crim_d"], src, decode(active2.fields["notes"])) - var/counter = 1 - while(active2.fields[text("com_[]", counter)]) - dat += text("[]
    Delete Entry

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

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

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

    ", src) - dat += text("\nDelete Record (ALL)

    \nPrint Record
    \nBack
    ", src, src, src) - if(4.0) - if(!Perp.len) - dat += text("ERROR. String could not be located.

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

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

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

    \nCriminal Status: []
    \n
    \nMinor Crimes: []
    \nDetails: []
    \n
    \nMajor Crimes: []
    \nDetails: []
    \n
    \nImportant Notes:
    \n\t[]
    \n
    \n
    Comments/Log

    ", record2.fields["criminal"], record2.fields["mi_crim"], record2.fields["mi_crim_d"], record2.fields["ma_crim"], record2.fields["ma_crim_d"], decode(record2.fields["notes"])) - var/counter = 1 - while(record2.fields[text("com_[]", counter)]) - P.info += text("[]
    ", record2.fields[text("com_[]", counter)]) - counter++ - else - P.info += "Security Record Lost!
    " - P.info += "" - printing = null - computer.updateUsrDialog() - //RECORD DELETE - if("Delete All Records") - temp = "" - temp += "Are you sure you wish to delete all Security records?
    " - temp += "Yes
    " - temp += "No" - - if("Purge All Records") - for(var/datum/data/record/R in data_core.security) - qdel(R) - temp = "All Security records deleted." - - if("Add Entry") - if(!istype(active2, /datum/data/record)) - return - var/a2 = active2 - var/t1 = sanitize(input("Add Comment:", "Secure. records", null, null) as message) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active2 != a2) - return - var/counter = 1 - while(active2.fields[text("com_[]", counter)]) - counter++ - active2.fields[text("com_[counter]")] = text("Made by [authenticated] ([rank]) on [time2text(world.realtime, "DDD MMM DD")] [stationtime2text()], [game_year]
    [t1]") - - if("Delete Record (ALL)") - if(active1) - temp = "
    Are you sure you wish to delete the record (ALL)?
    " - temp += "Yes
    " - temp += "No" - - if("Delete Record (Security)") - if(active2) - temp = "
    Are you sure you wish to delete the record (Security Portion Only)?
    " - temp += "Yes
    " - temp += "No" - - if("Delete Entry") - if(istype(active2, /datum/data/record) && active2.fields[text("com_[]", href_list["del_c"])]) - active2.fields[text("com_[]", href_list["del_c"])] = "Deleted" - //RECORD CREATE - if("New Record (Security)") - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - active2 = data_core.CreateSecurityRecord(active1.fields["name"], active1.fields["id"]) - screen = 3 - - if("New Record (General)") - active1 = data_core.CreateGeneralRecord() - active2 = null - - //FIELD FUNCTIONS - if("Edit Field") - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("name") - if(istype(active1, /datum/data/record)) - var/t1 = sanitizeName(input("Please input name:", "Secure. records", active1.fields["name"], null) as text) - if(!t1 || !length(trim(t1)) || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active1 != a1) - return - active1.fields["name"] = t1 - if("id") - if(istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active1 != a1) - return - active1.fields["id"] = t1 - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active1 != a1) - return - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active1 != a1) - return - active1.fields["age"] = t1 - if("mi_crim") - if(istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please input minor disabilities list:", "Secure. records", active2.fields["mi_crim"], null) as text) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active2 != a2) - return - active2.fields["mi_crim"] = t1 - if("mi_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize minor dis.:", "Secure. records", active2.fields["mi_crim_d"], null) as message) - if (!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active2 != a2) - return - active2.fields["mi_crim_d"] = t1 - if("ma_crim") - if(istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please input major diabilities list:", "Secure. records", active2.fields["ma_crim"], null) as text) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active2 != a2) - return - active2.fields["ma_crim"] = t1 - if("ma_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize major dis.:", "Secure. records", active2.fields["ma_crim_d"], null) as message) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active2 != a2) - return - active2.fields["ma_crim_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message, extra = 0) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active2 != a2) - return - active2.fields["notes"] = t1 - if("criminal") - if (istype(active2, /datum/data/record)) - temp = "
    Criminal Status:
    " - temp += "" - if("rank") - var/list/L = list( "Head of Personnel", "Colony Director", "AI" ) - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if ((istype(active1, /datum/data/record) && L.Find(rank))) - temp = "
    Rank:
    " - temp += "
      " - for(var/rank in joblist) - temp += "
    • [rank]
    • " - temp += "
    " - else - alert(usr, "You do not have the required rank to do this!") - if("species") - if (istype(active1, /datum/data/record)) - var/t1 = sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message) - if(!t1 || !authenticated || usr.stat || usr.restrained() || (!interactable() && !issilicon(usr)) || active1 != a1) - return - active1.fields["species"] = t1 - - //TEMPORARY MENU FUNCTIONS - else//To properly clear as per clear screen. - temp=null - switch(href_list["choice"]) - if("Change Rank") - if(active1) - active1.fields["rank"] = href_list["rank"] - if(href_list["rank"] in joblist) - active1.fields["real_rank"] = href_list["real_rank"] - - if("Change Criminal Status") - if(active2) - for(var/mob/living/carbon/human/H in player_list) - BITSET(H.hud_updateflag, WANTED_HUD) - switch(href_list["criminal2"]) - if("none") - active2.fields["criminal"] = "None" - if("arrest") - active2.fields["criminal"] = "*Arrest*" - if("incarcerated") - active2.fields["criminal"] = "Incarcerated" - if("parolled") - active2.fields["criminal"] = "Parolled" - if("released") - active2.fields["criminal"] = "Released" - - if("Delete Record (Security) Execute") - if(active2) - qdel(active2) - - if("Delete Record (ALL) Execute") - if(active1) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"]) - qdel(R) - else - qdel(active1) - if(active2) - qdel(active2) - else - temp = "This function does not appear to be working at the moment. Our apologies." - - //computer.updateUsrDialog() - interact() - return - -/obj/machinery/computer3/secure_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -/obj/machinery/computer3/secure_data/detective_computer - icon = 'icons/obj/computer.dmi' - icon_state = "messyfiles" diff --git a/code/game/machinery/computer3/computers/welcome.dm b/code/game/machinery/computer3/computers/welcome.dm deleted file mode 100644 index 801a6cd93c..0000000000 --- a/code/game/machinery/computer3/computers/welcome.dm +++ /dev/null @@ -1,34 +0,0 @@ -/obj/machinery/computer3/laptop/vended - default_prog = /datum/file/program/welcome - - -/datum/file/program/welcome - name = "Welcome Screen" - desc = "First time boot splash screen" - active_state = "osod" - image = 'icons/ntos/program.png' - - -/datum/file/program/welcome/interact() - usr.set_machine(src) - if(!interactable()) - return - var/dat = "" - dat += "
    Welcome to NTOS
    " - dat += "
    Thank you for choosing NTOS, your gateway to the future of mobile computing technology, sponsored by [using_map.company_name] (R)

    " - dat += "Getting started with NTOS:
    " - dat += "To leave a current program, click the X button in the top right corner of the window. This will return you to the NTOS desktop. \ - From the desktop, you can open the hard drive, usually located in the top left corner to access all the programs installed on your computer. \ - When you rented your laptop, you were supplied with programs that your [using_map.company_name] Issued ID has given you access to use. \ - In the event of a serious error, the right click menu will give you the ability to reset your computer. To open and close your laptop, alt-click your device.\ - If you have any questions or technical issues, please contact your local computer technical experts at your local [using_map.boss_name]." - popup.set_content(dat) - popup.set_title_image(usr.browse_rsc_icon(computer.icon, computer.icon_state)) - popup.open() - return - -/datum/file/program/welcome/Topic(href, href_list) - if(!interactable() || ..(href,href_list)) - return - interact() - return \ No newline at end of file diff --git a/code/game/machinery/computer3/file.dm b/code/game/machinery/computer3/file.dm deleted file mode 100644 index 528aff8c87..0000000000 --- a/code/game/machinery/computer3/file.dm +++ /dev/null @@ -1,166 +0,0 @@ -// I am deciding that for sayustation's purposes directories are right out, -// we can't even get backpacks to work right with recursion, and that -// actually fucking matters. Metadata too, that can be added if ever needed. - -/* - Files are datums that can be stored in digital storage devices -*/ - -/datum/file - var/name = "File" - var/extension = "dat" - var/volume = 10 // in KB - var/image = 'icons/ntos/file.png' // determines the icon to use, found in icons/ntos - var/obj/machinery/computer3/computer // the parent computer, if fixed - var/obj/item/part/computer/storage/device // the device that is containing this file - var/hidden_file = 0 // Prevents file from showing up on NTOS program list. - var/drm = 0 // Copy protection, called by copy() and move() - var/readonly = 0 // Edit protection, called by edit(), which is just a failcheck proc - -/datum/file/proc/execute(var/datum/file/source) - return - -// -// Copy file to device. -// If you overwrite this function, use the return value to make sure it succeeded -// -/datum/file/proc/copy(var/obj/item/part/computer/storage/dest) - if(!computer) return null - if(drm) - if(!computer.emagged) - return null - var/datum/file/F = new type() - if(!dest.addfile(F)) - return null // todo: arf here even though the player can't do a damn thing due to concurrency - return F - -// -// Move file to device -// Returns null on failure even though the existing file doesn't go away -// -/datum/file/proc/move(var/obj/item/part/computer/storage/dest) - if(!computer) return null - if(drm) - if(!computer.emagged) - return null - var/obj/item/part/computer/storage/current = device - if(!dest.addfile(src)) - return null - current.removefile(src) - return src - -// -// Determines if the file is editable. This does not use the DRM flag, -// but instead the readonly flag. -// - -/datum/file/proc/edit() - if(!computer) - return 0 - if(readonly && !computer.emagged) - return 0 - return 1 - -/* - CentCom root authorization certificate - - Non-destructive, officially sanctioned. - Has the same effect on computers as an emag. -*/ -/datum/file/centcom_auth - name = "CentCom Root Access Token" - extension = "auth" - volume = 100 - copy() - return null - -/* - A file that contains information -*/ - -/datum/file/data - - var/content = "content goes here" - var/file_increment = 1 - var/binary = 0 // determines if the file can't be opened by editor - -/datum/file/data/New() - if(content) - if(file_increment > 1) - volume = round(file_increment * length(content)) - ..() - -// Set the content to a specific amount, increase filesize appropriately. -/datum/file/data/proc/set_content(var/text) - content = text - if(file_increment > 1) - volume = round(file_increment * length(text)) - -/datum/file/data/copy(var/obj/O) - var/datum/file/data/D = ..(O) - if(D) - D.content = content - D.readonly = readonly - -/* - A generic file that contains text -*/ - -/datum/file/data/text - name = "Text File" - extension = "txt" - image = 'icons/ntos/file.png' - content = "" - file_increment = 0.002 // 0.002 kilobytes per character (1024 characters per KB) - -/datum/file/data/text/ClownProphecy - name = "Clown Prophecy" - content = "HONKhHONKeHONKlHONKpHONKHONmKHONKeHONKHONKpHONKlHONKeHONKaHONKsHONKe" - - -/* - A file that contains research -*/ - -/datum/file/data/research - name = "Untitled Research" - binary = 1 - content = "Untitled Tier X Research" - var/datum/tech/stored // the actual tech contents - volume = 1440 - -/* - A file that contains genetic information -*/ - -/datum/file/data/genome - name = "Genetic Buffer" - binary = 1 - var/real_name = "Poop" - - -/datum/file/data/genome/SE - name = "Structural Enzymes" - -/datum/file/data/genome/UE - name = "Unique Enzymes" - -/* -the way genome computers now work, a subtype is the wrong way to do this; -it will no longer be picked up. You can change this later if you need to. -for now put it on a disk - -/datum/file/data/genome/UE/GodEmperorOfMankind - name = "G.E.M.K." - content = "066000033000000000AF00330660FF4DB002690" - label = "God Emperor of Mankind" -*/ -/datum/file/data/genome/UI - name = "Unique Identifier" - -/datum/file/data/genome/UI/UE - name = "Unique Identifier + Unique Enzymes" - -/datum/file/data/genome/cloning - name = "Cloning Data" - var/datum/data/record/record diff --git a/code/game/machinery/computer3/laptop.dm b/code/game/machinery/computer3/laptop.dm deleted file mode 100644 index 9a8ba02b00..0000000000 --- a/code/game/machinery/computer3/laptop.dm +++ /dev/null @@ -1,183 +0,0 @@ -/* - Computer3 portable computer. - - Battery powered only; it does not use the APC network at all. - - When picked up, becomes an inert item. This item can be put in a recharger, - or set down and re-opened into the original machine. While closed, the computer - has the MAINT stat flag. If you want to ignore this, you will have to bitmask it out. - - The unused(?) alt+click will toggle laptops open and closed. If we find a better - answer for this in the future, by all means use it. I just don't want it limited - to the verb, which is SIGNIFICANTLY less accessible than shutting a laptop. - Ctrl-click would work for closing the machine, since it's anchored, but not for - opening it back up again. And obviously, I don't want to override shift-click. - There's no double-click because that's used in regular click events. Alt-click is the - only obvious one left. -*/ - - -/obj/item/device/laptop - name = "Laptop Computer" - desc = "A clamshell portable computer. It is closed." - icon = 'icons/obj/computer3.dmi' - icon_state = "laptop-closed" - pixel_x = 2 - pixel_y = -3 - w_class = ITEMSIZE_NORMAL - - var/obj/machinery/computer3/laptop/stored_computer = null - -/obj/item/device/laptop/get_cell() - return stored_computer.battery - -/obj/item/device/laptop/verb/open_computer() - set name = "Open Laptop" - set category = "Object" - set src in view(1) - - if(usr.stat || usr.restrained() || usr.lying || !istype(usr, /mob/living)) - to_chat(usr, "You can't do that.") - return - - if(!Adjacent(usr)) - to_chat(usr, "You can't reach it.") - return - - if(!istype(loc,/turf)) - to_chat(usr, "[src] is too bulky! You'll have to set it down.") - return - - if(!stored_computer) - if(contents.len) - for(var/obj/O in contents) - O.loc = loc - to_chat(usr, "\The [src] crumbles to pieces.") - spawn(5) - qdel(src) - return - - stored_computer.loc = loc - stored_computer.stat &= ~MAINT - stored_computer.update_icon() - loc = stored_computer - to_chat(usr, "You open \the [src].") - -/obj/item/device/laptop/AltClick() - if(Adjacent(usr)) - open_computer() - -//Quickfix until Snapshot works out how he wants to redo power. ~Z -/obj/item/device/laptop/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(stored_computer) - stored_computer.eject_id() - -/obj/machinery/computer3/laptop/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - var/obj/item/part/computer/cardslot/C = locate() in src.contents - - if(!C) - to_chat(usr, "There is no card port on the laptop.") - return - - C.remove(usr) - return - -/obj/machinery/computer3/laptop - name = "Laptop Computer" - desc = "A clamshell portable computer. It is open." - - icon_state = "laptop" - density = 0 - pixel_x = 2 - pixel_y = -3 - show_keyboard = 0 - active_power_usage = 200 // Stationary consoles we use on station have 300, laptops are probably slightly more power efficient - idle_power_usage = 100 - - var/obj/item/device/laptop/portable = null - -/obj/machinery/computer3/laptop/New(var/L, var/built = 0) - if(!built && !battery) - battery = new /obj/item/weapon/cell(src) - battery.maxcharge = 500 - battery.charge = 500 - ..(L,built) - -/obj/machinery/computer3/laptop/verb/close_computer() - set name = "Close Laptop" - set category = "Object" - set src in view(1) - - if(usr.stat || usr.restrained() || usr.lying || !istype(usr, /mob/living)) - to_chat(usr, "You can't do that.") - return - - if(!Adjacent(usr)) - to_chat(usr, "You can't reach it.") - return - - close_laptop(usr) - -/obj/machinery/computer3/laptop/proc/close_laptop(mob/user = null) - if(istype(loc,/obj/item/device/laptop)) - testing("Close closed computer") - return - if(!istype(loc,/turf)) - testing("Odd computer location: [loc] - close laptop") - return - - if(stat&BROKEN) - if(user) - to_chat(user, "\The [src] is broken! You can't quite get it closed.") - return - - if(!portable) - portable=new - portable.stored_computer = src - - portable.loc = loc - loc = portable - stat |= MAINT - if(user) - to_chat(user, "You close \the [src].") - -/obj/machinery/computer3/laptop/auto_use_power() - if(stat&MAINT) - return - if(use_power && istype(battery) && battery.charge > 0) - if(use_power == 1) - battery.use(idle_power_usage*CELLRATE) //idle and active_power_usage are in WATTS. battery.use() expects CHARGE. - else - battery.use(active_power_usage*CELLRATE) - return 1 - return 0 - -/obj/machinery/computer3/laptop/use_power(var/amount, var/chan = -1) - if(battery && battery.charge > 0) - battery.use(amount*CELLRATE) - -/obj/machinery/computer3/laptop/power_change() - if( !battery || battery.charge <= 0 ) - stat |= NOPOWER - else - stat &= ~NOPOWER - -/obj/machinery/computer3/laptop/Destroy() - if(istype(loc,/obj/item/device/laptop)) - var/obj/O = loc - spawn(5) - if(O) - qdel(O) - ..() - - -/obj/machinery/computer3/laptop/AltClick() - if(Adjacent(usr)) - close_computer() diff --git a/code/game/machinery/computer3/lapvend.dm b/code/game/machinery/computer3/lapvend.dm deleted file mode 100644 index 7c4e458832..0000000000 --- a/code/game/machinery/computer3/lapvend.dm +++ /dev/null @@ -1,374 +0,0 @@ -/obj/machinery/lapvend - name = "Laptop Vendor" - desc = "A generic vending machine." - icon = 'icons/obj/vending.dmi' - icon_state = "robotics" - anchored = 1 - density = 1 - var/obj/machinery/computer3/laptop/vended/newlap = null - var/obj/item/device/laptop/relap = null - var/vendmode = 0 - - var/cardreader = 0 - var/floppy = 0 - var/radionet = 0 - var/camera = 0 - var/network = 0 - var/power = 0 - - -/obj/machinery/lapvend/New() - ..() - spawn(4) - power_change() - return - return - - -/obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob) - var/obj/item/weapon/card/id/I = W.GetID() - - if(default_unfasten_wrench(user, W, 20)) - return - - if(vendmode == 1 && I) - scan_id(I, W) - vendmode = 0 - SSnanoui.update_uis(src) - if(vendmode == 2 && I) - if(reimburse_id(I, W)) - vendmode = 0 - SSnanoui.update_uis(src) - if(vendmode == 0) - if(istype(W, /obj/item/device/laptop)) - var/obj/item/device/laptop/L = W - relap = L - calc_reimburse(L) - usr.drop_item() - L.loc = src - vendmode = 2 - to_chat(user, "You slot your [L.name] into \The [src.name]") - SSnanoui.update_uis(src) - else - ..() - - -/obj/machinery/lapvend/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - - ui_interact(user) - -/** - * Display the NanoUI window for the vending machine. - * - * See NanoUI documentation for details. - */ -/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - - var/list/data = list() - data["mode"] = vendmode - data["cardreader"] = cardreader - data["floppy"] = floppy - data["radionet"] = radionet - data["camera"] = camera - data["network"] = network - data["power"] = power - data["total"] = total() - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "laptop_vendor.tmpl", src.name, 480, 425) - ui.set_initial_data(data) - ui.open() - //ui.set_auto_update(5) - -/obj/machinery/lapvend/Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) - return - if(usr.stat || usr.restrained()) - return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - switch(href_list["choice"]) - if("single_add") - cardreader = 1 - if ("dual_add") - cardreader = 2 - if ("floppy_add") - floppy = 1 - if ("radio_add") - radionet = 1 - if ("camnet_add") - camera = 1 - if ("area_add") - network = 1 - if ("prox_add") - network = 2 - if ("cable_add") - network = 3 - if ("high_add") - power = 1 - if ("super_add") - power = 2 - - if ("cardreader_rem") - cardreader = 0 - if ("floppy_rem") - floppy = 0 - if ("radio_rem") - radionet = 0 - if ("camnet_rem") - camera = 0 - if ("network_rem") - network = 0 - if ("power_rem") - power = 0 - - if("vend") - vendmode = 1 - - if("cancel") - if(relap) - relap.loc = src.loc - relap = null - vendmode = 0 - - src.add_fingerprint(usr) - SSnanoui.update_uis(src) - -/obj/machinery/lapvend/proc/vend() - if(cardreader > 0) - if(cardreader == 1) - newlap.spawn_parts += (/obj/item/part/computer/cardslot) - else - newlap.spawn_parts += (/obj/item/part/computer/cardslot/dual) - if(floppy == 1) - newlap.spawn_parts += (/obj/item/part/computer/storage/removable) - if(radionet == 1) - newlap.spawn_parts += (/obj/item/part/computer/networking/radio) - if(camera == 1) - newlap.spawn_parts += (/obj/item/part/computer/networking/cameras) - if (network == 1) - newlap.spawn_parts += (/obj/item/part/computer/networking/area) - if (network == 2) - newlap.spawn_parts += (/obj/item/part/computer/networking/prox) - if (network == 3) - newlap.spawn_parts += (/obj/item/part/computer/networking/cable) - if (power == 1) - newlap.battery.maxcharge = 1000 - newlap.battery.charge = 1000 - if (power == 2) - newlap.battery.maxcharge = 1750 - newlap.battery.charge = 1750 - - newlap.spawn_parts() - -/obj/machinery/lapvend/proc/scan_id(var/obj/item/weapon/card/id/C, var/obj/item/I) - visible_message("\The [usr] swipes \the [I] through \the [src].") - var/datum/money_account/CH = get_account(C.associated_account_number) - if(!CH) - to_chat(usr, "\icon[src]No valid account number is associated with this card.") - return - if(CH.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) - if(vendor_account) - var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - var/datum/money_account/D = attempt_account_access(C.associated_account_number, attempt_pin, 2) - if(D) - transfer_and_vend(D, C) - else - to_chat(usr, "\icon[src]Unable to access vendor account. Please record the machine ID and call [using_map.boss_short] Support.") - else - to_chat(usr, "\icon[src]Unable to access vendor account. Please record the machine ID and call [using_map.boss_short] Support.") - else - transfer_and_vend(CH, C) - - -// Transfers money and vends the laptop. -/obj/machinery/lapvend/proc/transfer_and_vend(var/datum/money_account/D, var/obj/item/weapon/card/C) - var/transaction_amount = total() - if(transaction_amount <= D.money) - - //transfer the money - D.money -= transaction_amount - vendor_account.money += transaction_amount - //Transaction logs - var/datum/transaction/T = new() - T.target_name = "[vendor_account.owner_name] (via [src.name])" - T.purpose = "Purchase of Laptop" - if(transaction_amount > 0) - T.amount = "([transaction_amount])" - else - T.amount = "[transaction_amount]" - T.source_terminal = src.name - T.date = current_date_string - T.time = stationtime2text() - D.transaction_log.Add(T) - // - T = new() - T.target_name = D.owner_name - T.purpose = "Purchase of Laptop" - T.amount = "[transaction_amount]" - T.source_terminal = src.name - T.date = current_date_string - T.time = stationtime2text() - vendor_account.transaction_log.Add(T) - - newlap = new /obj/machinery/computer3/laptop/vended(src.loc) - - choose_progs(C) - vend() - newlap.close_laptop() - newlap = null - cardreader = 0 - floppy = 0 - radionet = 0 - camera = 0 - network = 0 - power = 0 - else - to_chat(usr, "\icon[src]You don't have that much money!") - -/obj/machinery/lapvend/proc/total() - var/total = 0 - - if(cardreader == 1) - total += 50 - if(cardreader == 2) - total += 125 - if(floppy == 1) - total += 50 - if(radionet == 1) - total += 50 - if(camera == 1) - total += 100 - if(network == 1) - total += 75 - if(network == 2) - total += 50 - if(network == 3) - total += 25 - if(power == 1) - total += 175 - if(power == 2) - total += 250 - - return total - -/obj/machinery/lapvend/proc/choose_progs(var/obj/item/weapon/card/id/C) - if(access_security in C.access) - newlap.spawn_files += (/datum/file/program/secure_data) - newlap.spawn_files += (/datum/file/camnet_key) - newlap.spawn_files += (/datum/file/program/security) - if(access_armory in C.access) - newlap.spawn_files += (/datum/file/program/prisoner) - if(access_atmospherics in C.access) - newlap.spawn_files += (/datum/file/program/atmos_alert) - if(access_change_ids in C.access) - newlap.spawn_files += (/datum/file/program/card_comp) - if(access_heads in C.access) - newlap.spawn_files += (/datum/file/program/communications) - if((access_medical in C.access) || (access_forensics_lockers in C.access)) //Gives detective the medical records program, but not the crew monitoring one. - newlap.spawn_files += (/datum/file/program/med_data) - if (access_medical in C.access) - newlap.spawn_files += (/datum/file/program/crew) - if(access_engine in C.access) - newlap.spawn_files += (/datum/file/program/powermon) - if(access_research in C.access) - newlap.spawn_files += (/datum/file/camnet_key/research) - newlap.spawn_files += (/datum/file/camnet_key/bombrange) - newlap.spawn_files += (/datum/file/camnet_key/xeno) - if(access_rd in C.access) - newlap.spawn_files += (/datum/file/program/borg_control) - if(access_cent_specops in C.access) - newlap.spawn_files += (/datum/file/camnet_key/creed) - newlap.spawn_files += (/datum/file/program/arcade) - newlap.spawn_files += (/datum/file/camnet_key/entertainment) - //Atlantis: Each laptop gets "invisible" program/security - REQUIRED for camnetkeys to work. - newlap.spawn_files += (/datum/file/program/security/hidden) - newlap.update_spawn_files() - -/obj/machinery/lapvend/proc/calc_reimburse(var/obj/item/device/laptop/L) - if(istype(L.stored_computer.cardslot,/obj/item/part/computer/cardslot)) - cardreader = 1 - if(istype(L.stored_computer.cardslot,/obj/item/part/computer/cardslot/dual)) - cardreader = 2 - if(istype(L.stored_computer.floppy,/obj/item/part/computer/storage/removable)) - floppy = 1 - if(istype(L.stored_computer.radio,/obj/item/part/computer/networking/radio)) - radionet = 1 - if(istype(L.stored_computer.camnet,/obj/item/part/computer/networking/cameras)) - camera = 1 - if(istype(L.stored_computer.net,/obj/item/part/computer/networking/area)) - network = 1 - if(istype(L.stored_computer.net,/obj/item/part/computer/networking/prox)) - network = 2 - if(istype(L.stored_computer.net,/obj/item/part/computer/networking/cable)) - network = 3 - if(istype(L.stored_computer.battery, /obj/item/weapon/cell/high)) - power = 1 - if(istype(L.stored_computer.battery, /obj/item/weapon/cell/super)) - power = 2 - - - -/obj/machinery/lapvend/proc/reimburse_id(var/obj/item/weapon/card/id/C, var/obj/item/I) - visible_message("\The [usr] swipes \the [I] through \the [src].") - var/datum/money_account/CH = get_account(C.associated_account_number) - if(!CH) - to_chat(usr, "\icon[src]No valid account number is associated with this card.") - return 0 - if(CH.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) - if(vendor_account) - var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - var/datum/money_account/D = attempt_account_access(C.associated_account_number, attempt_pin, 2) - if(D) - transfer_and_reimburse(D) - return 1 - else - to_chat(usr, "\icon[src]Unable to access vendor account. Please record the machine ID and call [using_map.boss_short] Support.") - return 0 - else - to_chat(usr, "\icon[src]Unable to access vendor account. Please record the machine ID and call [using_map.boss_short] Support.") - return 0 - else - transfer_and_reimburse(CH) - return 1 - -/obj/machinery/lapvend/proc/transfer_and_reimburse(var/datum/money_account/D) - var/transaction_amount = total() - //transfer the money - D.money += transaction_amount - vendor_account.money -= transaction_amount - - //Transaction logs - var/datum/transaction/T = new() - T.target_name = "[vendor_account.owner_name] (via [src.name])" - T.purpose = "Return purchase of Laptop" - if(transaction_amount > 0) - T.amount = "([transaction_amount])" - else - T.amount = "[transaction_amount]" - T.source_terminal = src.name - T.date = current_date_string - T.time = stationtime2text() - D.transaction_log.Add(T) - // - T = new() - T.target_name = D.owner_name - T.purpose = "Return purchase of Laptop" - T.amount = "[transaction_amount]" - T.source_terminal = src.name - T.date = current_date_string - T.time = stationtime2text() - vendor_account.transaction_log.Add(T) - - qdel(relap) - vendmode = 0 - cardreader = 0 - floppy = 0 - radionet = 0 - camera = 0 - network = 0 - power = 0 diff --git a/code/game/machinery/computer3/networking.dm b/code/game/machinery/computer3/networking.dm deleted file mode 100644 index 7b7922a635..0000000000 --- a/code/game/machinery/computer3/networking.dm +++ /dev/null @@ -1,243 +0,0 @@ -/obj/item/part/computer/networking - name = "Computer networking component" - -/obj/item/part/computer/networking/allow_attackby(var/obj/item/I, var/mob/user) - return 0 - -/* - This is the public-facing proc used by NETUP. - It does additional checking before and after calling get_machines() -*/ -/obj/item/part/computer/networking/proc/connect_to(var/typekey,var/atom/previous) - if(!computer || computer.stat) - return null - - if(istype(previous,typekey) && verify_machine(previous)) - return previous - - var/result = get_machines(typekey) - - if(!result) - return null - - if(islist(result)) - var/list/R = result - if(R.len == 0) - return null - else if(R.len == 1) - return R[1] - else - var/list/atomlist = computer.format_atomlist(R) - result = input("Select:","Multiple destination machines located",atomlist[1]) as null|anything in atomlist - return atomlist[result] - - if(isobj(result)) - return result - - return null // ? - -/* - This one is used to determine the candidate machines. - It may return an object, a list of objects, or null. - - Overwite this on any networking component. -*/ -/obj/item/part/computer/networking/proc/get_machines(var/typekey) - return list() - -/* - This is used to verify that an existing machine is within the network. - Calling NETUP() with an object argument will run this check, and if - the object is still accessible, it will be used. Otherwise, another - search will be run. - - Overwrite this on any networking component. -*/ -/obj/item/part/computer/networking/proc/verify_machine(var/obj/previous) - return 0 - -/* - Provides radio/signaler functionality, and also - network-connects to anything on the same z-level - which is tuned to the same frequency. -*/ -/obj/item/part/computer/networking/radio - name = "Wireless networking component" - desc = "Radio module for computers" - - var/datum/radio_frequency/radio_connection = null - var/frequency = PUB_FREQ - var/rad_filter = null - var/range = null - var/subspace = 0 - -/obj/item/part/computer/networking/radio/init() - ..() - spawn(5) - radio_connection = radio_controller.add_object(src, src.frequency, src.rad_filter) - -/obj/item/part/computer/networking/radio/proc/set_frequency(new_frequency) - if(radio_controller) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, rad_filter) - else - frequency = new_frequency - spawn(rand(5,10)) - set_frequency(new_frequency) - -/obj/item/part/computer/networking/radio/receive_signal(var/datum/signal/signal) - if(!signal || !computer || (computer.stat&~MAINT)) // closed laptops use maint, allow it - return - if(computer.program) - computer.program.receive_signal(signal) - -/obj/item/part/computer/networking/radio/proc/post_signal(var/datum/signal/signal) - if(!computer || (computer.stat&~MAINT) || !computer.program) - return - if(!radio_connection) - return - radio_connection.post_signal(src,signal,rad_filter,range) - -/obj/item/part/computer/networking/radio/get_machines(var/typekey) - if(!radio_connection || !radio_connection.frequency) - return list() - var/list/result = list() - var/turf/T = get_turf(loc) - var/z_level = T.z - for(var/obj/O in radio_connection.devices) - if(istype(O,typekey)) - T = get_turf(O) - if(istype(O) && (subspace || (O.z == z_level))) // radio does not work across z-levels - result |= O - return result - -/obj/item/part/computer/networking/radio/verify_machine(var/obj/previous) - if(!previous) - return 0 - if(subspace) - return ( radio_connection && (previous in radio_connection.devices) ) - else - var/turf/T = get_turf(loc) - var/turf/O = get_turf(previous) - if(!T || !O) - return 0 - return ( radio_connection && (previous in radio_connection.devices) && (T.z == O.z)) - -/* - Subspace networking: Communicates off-station. Allows centcom communications. -*/ -/obj/item/part/computer/networking/radio/subspace - name = "subspace networking terminal" - desc = "Communicates long distances and through spatial anomalies." - subspace = 1 - -/* - APC (/area) networking -*/ - -/obj/item/part/computer/networking/area - name = "short-wave networking terminal" - desc = "Connects to nearby computers through the area power network" - -/obj/item/part/computer/networking/area/get_machines(var/typekey) - var/area/A = get_area(loc) - if(!istype(A) || A == /area) - return list() - if(typekey == null) - typekey = /obj/machinery - var/list/machines = list() - for(var/obj/O in A.contents) - if(istype(O,typekey)) - machines |= O - return machines - -/obj/item/part/computer/networking/area/verify_machine(var/obj/previous) - if(!previous) return 0 - var/area/A = get_area(src) - if( A && A == get_area(previous) ) - return 1 - return 0 - -/* - Proximity networking: Connects to machines or computers adjacent to this device -*/ -/obj/item/part/computer/networking/prox - name = "proximity networking terminal" - desc = "Connects a computer to adjacent machines" - -/obj/item/part/computer/networking/prox/get_machines(var/typekey) - var/turf/T = get_turf(loc) - if(!istype(T)) - return list() - if(typekey == null) - typekey = /obj/machinery - var/list/nearby_machines = list() - for(var/obj/O in T) - if(istype(O,typekey)) - nearby_machines += O - for(var/d in cardinal) - var/turf/T2 = get_step(T,d) - for(var/obj/O in T2) - if(istype(O,typekey)) - nearby_machines += O - return nearby_machines - -/obj/item/part/computer/networking/prox/verify_machine(var/obj/previous) - if(!previous) - return 0 - if(get_dist(get_turf(previous),get_turf(loc)) == 1) - return 1 - return 0 - -/* - Cable networking: Not currently used -*/ -/obj/item/part/computer/networking/cable - name = "cable networking terminal" - desc = "Connects to other machines on the same cable network." - -/obj/item/part/computer/networking/cable/get_machines(var/typekey) -// if(istype(computer,/obj/machinery/computer/laptop)) // laptops move, this could get breaky -// return list() - var/turf/T = get_turf(loc) - var/datum/powernet/P = null - for(var/obj/structure/cable/C in T) - if(C.d1 == 0) - P = C.powernet - break - if(!P) - return list() - if(!typekey) - typekey = /obj/machinery - else if(typekey == /datum/powernet) - return list(P) - var/list/candidates = list() - for(var/atom/A in P.nodes) - if(istype(A,typekey)) - candidates += A - else if(istype(A,/obj/machinery/power/terminal)) - var/obj/machinery/power/terminal/PT = A - if(istype(PT.master,typekey)) - candidates += PT.master - return candidates - -/obj/item/part/computer/networking/cable/verify_machine(var/obj/previous) - if(!previous) - return 0 - var/turf/T = get_turf(loc) - var/datum/powernet/P = null - for(var/obj/structure/cable/C in T) - if(C.d1 == 0) - P = C.powernet - break - if(istype(previous,/datum/powernet)) - if(previous == P) - return 1 - return 0 - T = get_turf(previous.loc) - for(var/obj/structure/cable/C in T) - if(C.d1 == 0 && (C.powernet == P)) - return 1 - return 0 - diff --git a/code/game/machinery/computer3/program.dm b/code/game/machinery/computer3/program.dm deleted file mode 100644 index bf84c1622e..0000000000 --- a/code/game/machinery/computer3/program.dm +++ /dev/null @@ -1,404 +0,0 @@ - -/* -Programs are a file that can be executed -*/ - -/datum/file/program - name = "Untitled" - extension = "prog" - image = 'icons/ntos/program.png' - var/desc = "An unidentifiable program." - - var/image/overlay = null // Icon to be put on top of the computer frame. - - var/active_state = "generic" // the icon_state that the computer goes to when the program is active - - drm = 0 // prevents a program from being copied - var/refresh = 0 // if true, computer does screen updates during process(). - var/error = 0 // set by background programs so an error pops up when used - - var/human_controls = 0 // if true, non-human animals cannot interact with this program (monkeys, xenos, etc) - var/ai_allowed = 1 // if true, silicon mobs (AI/cyborg) are allowed to use this program. - - var/datum/browser/popup = null - - // ID access: Note that computer3 does not normally check your ID. - // By default this is only really used for inserted cards. - var/list/req_access = list() // requires all of these UNLESS below succeeds - var/list/req_one_access = list() // requires one of these - - -/datum/file/program/New() - ..() - if(!active_state) - active_state = "generic" - overlay = image('icons/obj/computer3.dmi',icon_state = active_state) - - -/datum/file/program/proc/decode(text) - //adds line breaks - text = replacetext(text, "\n","
    ") - return text - - - -/datum/file/program/execute(var/datum/file/source) - if(computer && !computer.stat) - computer.program = src - computer.req_access = req_access - computer.req_one_access = req_one_access - update_icon() - computer.update_icon() - if(usr) - usr << browse(null, "window=\ref[computer]") - computer.attack_hand(usr) - - ..() - -/* - Standard Topic() for links -*/ - -/datum/file/program/Topic(href, href_list) - return - -/* - The computer object will transfer all empty-hand calls to the program (this includes AIs, Cyborgs, and Monkies) -*/ -/datum/file/program/proc/interact() - return - -/* - Standard receive_signal() -*/ - -/datum/file/program/proc/receive_signal(var/datum/signal/signal) - return -/* - The computer object will transfer all attackby() calls to the program - If the item is a valid interactable object, return 1. Else, return 0. - This helps identify what to use to actually hit the computer with, and - what can be used to interact with it. - - Screwdrivers will, by default, never call program/attackby(). That's used - for deconstruction instead. -*/ - - -/datum/file/program/proc/attackby(O as obj, user as mob) - return - -/* - Try not to overwrite this proc, I'd prefer we stayed - with interact() as the main proc -*/ -/datum/file/program/proc/attack_hand(mob/user as mob) - usr = user - interact() - -/* - Called when the computer is rebooted or the program exits/restarts. - Be sure not to save any work. Do NOT start the program again. - If it is the os, the computer will run it again automatically. - - Also, we are deleting the browser window on the chance that this is happening - when the computer is damaged or disassembled, causing us to lose our computer. - The popup window's title is a reference to the computer, making it unique, so - it could introduce bugs in that case. -*/ -/datum/file/program/proc/Reset() - error = 0 - update_icon() - if(popup) - popup.close() - qdel(popup) - return - -/* - The computer object will transfer process() calls to the program. -*/ -/datum/file/program/process() - if(refresh && computer && !computer.stat) - computer.updateDialog() - update_icon() - -/datum/file/program/proc/update_icon() - return - -/datum/file/program/proc/check_access(obj/item/I) - if( (!istype(req_access) || !req_access.len) && (!istype(req_one_access) || !req_one_access.len) ) //no requirements - return 1 - - if(!I) - return 0 - - var/list/iAccess = I.GetAccess() - if(!iAccess || !iAccess.len) - return 0 - - var/list/temp = req_one_access & iAccess - if(temp.len) // a required access in item access list - return 1 - temp = req_access - iAccess - if(temp.len) // a required access not in item access list - return 0 - return 1 - - -/* - Because this does sanity checks I have added the code to make a popup here. - It also does sanity checks there that should prevent some edge case madness. -*/ -/datum/file/program/proc/interactable(var/mob/user = usr) - if(computer && computer.interactable(user)) - if(!popup) - popup = new(user, "\ref[computer]", name, nref=src) - popup.set_title_image(usr.browse_rsc_icon(overlay.icon, overlay.icon_state)) - popup.set_title_buttons(topic_link(src,"quit","")) - if(popup.user != user) - popup.user = user - popup.set_title_image(usr.browse_rsc_icon(overlay.icon, overlay.icon_state)) - popup.set_title(name) - return 1 - return 0 - - -/datum/file/program/proc/fake_link(var/text) - return "[text]" - -/* - Meant for text (not icons) - - lists all installed drives and their files - - I am NOT adding a computer sanity check here, - because why the flying fuck would you get to this - proc before having run it at least once? - If you cause runtimes with this function - may the shame of all ages come upon you. -*/ -/datum/file/program/proc/list_all_files_by_drive(var/typekey,var/linkop = "runfile") - var/dat = "" - if(!typekey) typekey = /datum/file - if(computer.hdd) - dat += "

    [computer.hdd]

    " - for(var/datum/file/F in computer.hdd.files) - if(istype(F,typekey)) - dat += topic_link(src,"[linkop]=\ref[F]",F.name) + "
    " - if(computer.hdd.files.len == 0) - dat += "No files
    " - dat += "
    " - - if(computer.floppy) - if(!computer.floppy.inserted) - dat += "

    [computer.floppy] - Eject



    " - else - dat += "

    [computer.floppy] - [topic_link(src,"eject_disk","Eject")]

    " - for(var/datum/file/F in computer.floppy.inserted.files) - dat += topic_link(src,"[linkop]=\ref[F]",F.name) + "
    " - if(computer.floppy.inserted.files.len == 0) - dat += "No files
    " - dat += "
    " - - if(computer.cardslot && istype(computer.cardslot.reader,/obj/item/weapon/card/data)) - dat += "

    [computer.cardslot.reader] - [topic_link(src,"eject_card=reader","Eject")]

    " - var/obj/item/weapon/card/data/D = computer.cardslot.reader - for(var/datum/file/F in D.files) - dat += topic_link(src,"[linkop]=\ref[F]",F.name) + "
    " - if(D.files.len == 0) - dat += "No files
    " - return dat - -// You don't NEED to use this version of topic() for this, you can do it yourself if you prefer -// If you do, do the interactable() check first, please, I don't want to repeat it here. It's not hard. -/datum/file/program/Topic(var/href,var/list/href_list) - if(!computer) - return 0 - - // - // usage: eject_disk - // only functions if there is a removable drive - // - if("eject_disk" in href_list) - if(computer.floppy) - computer.floppy.eject_disk() - return 1 - // - // usage: eject_card | eject_card=reader | eject_card=writer - // only functions if there is a cardslot - // - if("eject_card" in href_list) - if(computer.cardslot) - if(istype(computer.cardslot, /obj/item/part/computer/cardslot/dual) && href_list["eject_card"] == "writer") - computer.cardslot.remove(usr) - else - computer.cardslot.remove(usr) - return 1 - // - // usage: runfile=\ref[file] - // executes the file - // - if("runfile" in href_list) - var/datum/file/F = locate(href_list["runfile"]) - if(istype(F) && F.computer == computer) - F.execute(src) - return 1 - - if("close" in href_list) - usr.unset_machine() - popup.close() - return 1 - // - // usage: quit - // unloads the program, returning control to the OS - // - if("quit" in href_list) - computer.program = null - usr << browse(null,"window=\ref[computer]") // ntos will need to resize the window - computer.update_icon() - computer.updateDialog() - return 1 - return 0 - - -/datum/file/program/RD - name = "R&D Manager" - image = 'icons/ntos/research.png' - desc = "A software suit for generic research and development machinery interaction. Comes pre-packaged with extensive cryptographic databanks for secure connections with external devices." - active_state = "rdcomp" - volume = 11000 - -/datum/file/program/RDserv - name = "R&D Server" - image = 'icons/ntos/server.png' - active_state = "rdcomp" - volume = 9000 - -/datum/file/program/SuitSensors - name = "Crew Monitoring" - image = 'icons/ntos/monitoring.png' - active_state = "crew" - volume = 3400 - -/datum/file/program/Genetics - name = "Genetics Suite" - image = 'icons/ntos/genetics.png' - desc = "A sophisticated software suite containing read-only genetics hardware specifications and a highly compressed genome databank." - active_state = "dna" - volume = 8000 - -/datum/file/program/Cloning - name = "Cloning Platform" - image = 'icons/ntos/cloning.png' - desc = "A software platform for accessing external cloning apparatus." - active_state = "dna" - volume = 7000 - -/datum/file/program/TCOMmonitor - name = "TComm Monitor" - image = 'icons/ntos/tcomms.png' - active_state = "comm_monitor" - volume = 5500 - -/datum/file/program/TCOMlogs - name = "TComm Log View" - image = 'icons/ntos/tcomms.png' - active_state = "comm_logs" - volume = 5230 - -/datum/file/program/TCOMtraffic - name = "TComm Traffic" - image = 'icons/ntos/tcomms.png' - active_state = "generic" - volume = 8080 - -/datum/file/program/securitycam - name = "Sec-Cam Viewport" - image = 'icons/ntos/camera.png' - drm = 1 - active_state = "cameras" - volume = 2190 - -/datum/file/program/securityrecords - name = "Security Records" - image = 'icons/ntos/records.png' - drm = 1 - active_state = "security" - volume = 2520 - -/datum/file/program/medicalrecords - name = "Medical Records" - image = 'icons/ntos/medical.png' - drm = 1 - active_state = "medcomp" - volume = 5000 - -/datum/file/program/SMSmonitor - name = "Messaging Monitor" - image = 'icons/ntos/pda.png' - active_state = "comm_monitor" - volume = 3070 - -/datum/file/program/OperationMonitor - name = "OR Monitor" - image = 'icons/ntos/operating.png' - active_state = "operating" - volume = 4750 - -/datum/file/program/PodLaunch - name = "Pod Launch" - active_state = "computer_generic" - volume = 520 - -/datum/file/program/powermon - name = "Power Grid" - image = 'icons/ntos/power.png' - active_state = "power" - volume = 7200 - -/datum/file/program/prisoner - name = "Prisoner Control" - image = 'icons/ntos/prison.png' - drm = 1 - active_state = "power" - volume = 5000 - -/datum/file/program/borg_control - name = "Cyborg Maint" - image = 'icons/ntos/borgcontrol.png' - active_state = "robot" - volume = 9050 - -/datum/file/program/AIupload - name = "AI Upload" - image = 'icons/ntos/aiupload.png' - active_state = "command" - volume = 5000 - -/datum/file/program/Cyborgupload - name = "Cyborg Upload" - image = 'icons/ntos/borgupload.png' - active_state = "command" - volume = 5000 - -/datum/file/program/Exosuit - name = "Exosuit Monitor" - image = 'icons/ntos/exocontrol.png' - active_state = "mecha" - volume = 7000 - -/datum/file/program/EmergencyShuttle - name = "Shuttle Console" - active_state = "shuttle" - volume = 10000 - -/datum/file/program/Stationalert - name = "Alert Monitor" - image = 'icons/ntos/alerts.png' - active_state = "computer_generic" - volume = 10150 - - - - - - diff --git a/code/game/machinery/computer3/program_disks.dm b/code/game/machinery/computer3/program_disks.dm deleted file mode 100644 index e9a649499b..0000000000 --- a/code/game/machinery/computer3/program_disks.dm +++ /dev/null @@ -1,51 +0,0 @@ - - -/obj/item/weapon/disk/file/arcade - name = "Arcade game grab pack" - desc = "A program install disk." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/arcade,/datum/file/program/arcade,/datum/file/program/arcade,/datum/file/program/arcade) - -/*/obj/item/weapon/disk/file/aifixer - name = "AI System Integrity Restorer" - desc = "A program install disk." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/aifixer)*/ - -/obj/item/weapon/disk/file/atmos_alert - name = "Atmospheric Alert Notifier" - desc = "A program install disk." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/atmos_alert) - -/obj/item/weapon/disk/file/cameras - name = "Camera Viewer" - desc = "A program install disk." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/security) - -/obj/item/weapon/disk/file/cameras/syndicate - name = "Camera Viewer" - desc = "A program install disk. A crude skull has been drawn on it and there is a list of items:\nFloppy Drive\nCamera Card\nNetwork Card: Adjacent\nPosition laptop nearby camera, enjoy." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/security/syndicate) - -/obj/item/weapon/disk/file/card - name = "ID Card Modifier" - desc = "A program install disk." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/card_comp) -/* -/obj/item/weapon/disk/file/genetics - name = "Genetics & Cloning" - desc = "A program install disk." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "datadisk_arcade" - spawn_files = list(/datum/file/program/cloning,/datum/file/program/dnascanner) -*/ diff --git a/code/game/machinery/computer3/server.dm b/code/game/machinery/computer3/server.dm deleted file mode 100644 index 5c820ce0ca..0000000000 --- a/code/game/machinery/computer3/server.dm +++ /dev/null @@ -1,32 +0,0 @@ -/* - Todo: - I can probably get away with a global list on servers that contains database sort of stuff - (replacing the datacore probably) - with the justification that they loadbalance and duplicate data across each other. As long as - one server-type computer exists, the station will still have access to datacore-type info. - - I can doubtless use this for station alerts as well, which is good, because I was sort of - wondering how the hell I was going to port that. - - Also todo: Server computers should maybe generate heat the way the R&D server does? - At least the rack computer probably should. -*/ - -/obj/machinery/computer3/server - name = "server" - icon = 'icons/obj/computer3.dmi' - icon_state = "serverframe" - show_keyboard = 0 - -/obj/machinery/computer3/server/rack - name = "server rack" - icon_state = "rackframe" - - spawn_parts = list(/obj/item/part/computer/storage/hdd,/obj/item/part/computer/networking/radio/subspace) - -/obj/machinery/computer3/server/rack/update_icon() - //overlays.Cut() - return - -/obj/machinery/computer3/server/rack/attack_hand() // Racks have no screen, only AI can use them - return diff --git a/code/game/machinery/computer3/storage.dm b/code/game/machinery/computer3/storage.dm deleted file mode 100644 index 671d28c3a6..0000000000 --- a/code/game/machinery/computer3/storage.dm +++ /dev/null @@ -1,181 +0,0 @@ -/* - Computer devices that can store programs, files, etc. -*/ - -/obj/item/part/computer/storage - name = "Storage Device" - desc = "A device used for storing and retrieving digital information." - - // storage capacity, kb - var/volume = 0 - var/max_volume = 64 // should be enough for anyone - - var/driveletter = null // drive letter according to the computer - - var/list/files = list() // a list of files in the memory (ALL files) - var/removeable = 0 // determinse if the storage device is a removable hard drive (ie floppy) - - - var/writeprotect = 0 // determines if the drive forbids writing. - // note that write-protect is hardware and does not respect emagging. - - var/list/spawnfiles = list()// For mappers, special drives, and data disks - -/obj/item/part/computer/storage/New() - ..() - if(islist(spawnfiles)) - if(removeable && spawnfiles.len) - var/obj/item/part/computer/storage/removable/R = src - R.inserted = new(src) - if(writeprotect) - R.inserted.writeprotect = 1 - for(var/typekey in spawnfiles) - addfile(new typekey(),1) - -// Add a file to the hard drive, returns 0 if failed -// forced is used when spawning files on a write-protect drive -/obj/item/part/computer/storage/proc/addfile(var/datum/file/F,var/forced = 0) - if(!F || (F in files)) - return 1 - if(writeprotect && !forced) - return 0 - if(volume + F.volume > max_volume) - if(!forced) - return 0 - max_volume = volume + F.volume - - files.Add(F) - volume += F.volume - F.computer = computer - F.device = src - return 1 -/obj/item/part/computer/storage/proc/removefile(var/datum/file/F,var/forced = 0) - if(!F || !(F in files)) - return 1 - if(writeprotect && !forced) - return 0 - - files -= F - volume -= F.volume - if(F.device == src) - F.device = null - F.computer = null - return 1 - -/obj/item/part/computer/storage/init(var/obj/machinery/computer/target) - computer = target - for(var/datum/file/F in files) - F.computer = computer - -/* - Standard hard drives for computers. Used in computer construction -*/ -/obj/item/part/computer/storage/hdd - name = "Hard Drive" - max_volume = 25000 - icon_state = "hdd1" - - -/obj/item/part/computer/storage/hdd/big - name = "Big Hard Drive" - max_volume = 50000 - icon_state = "hdd2" - -/obj/item/part/computer/storage/hdd/gigantic - name = "Gigantic Hard Drive" - max_volume = 75000 - icon_state = "hdd3" - -/* - Removeable hard drives for portable storage -*/ - -/obj/item/part/computer/storage/removable - name = "Disk Drive" - max_volume = 3000 - removeable = 1 - - attackby_types = list(/obj/item/weapon/disk/file, /obj/item/weapon/pen) - var/obj/item/weapon/disk/file/inserted = null - -/obj/item/part/computer/storage/removable/proc/eject_disk(var/forced = 0) - if(!forced) - return - files = list() - inserted.loc = computer.loc - if(usr) - if(!usr.get_active_hand()) - usr.put_in_active_hand(inserted) - else if(forced && !usr.get_inactive_hand()) - usr.put_in_inactive_hand(inserted) - for(var/datum/file/F in inserted.files) - F.computer = null - inserted = null - - -/obj/item/part/computer/storage/removable/attackby(obj/O as obj, mob/user as mob) - if(inserted && istype(O,/obj/item/weapon/pen)) - to_chat(usr, "You use [O] to carefully pry [inserted] out of [src].") - eject_disk(forced = 1) - return - - if(istype(O,/obj/item/weapon/disk/file)) - if(inserted) - to_chat(usr, "There's already a disk in [src]!") - return - - to_chat(usr, "You insert [O] into [src].") - usr.drop_item() - O.loc = src - inserted = O - writeprotect = inserted.writeprotect - - files = inserted.files - for(var/datum/file/F in inserted.files) - F.computer = computer - - return - ..() - -/obj/item/part/computer/storage/removable/addfile(var/datum/file/F) - if(!F || !inserted) - return 0 - - if(F in inserted.files) - return 1 - - if(inserted.volume + F.volume > inserted.max_volume) - return 0 - - inserted.files.Add(F) - F.computer = computer - F.device = inserted - return 1 - -/* - Removable hard drive presents... - removeable disk! -*/ -/obj/item/weapon/disk/file - //parent_type = /obj/item/part/computer/storage // todon't: do this - name = "Data Disk" - desc = "A device that can be inserted and removed into computers easily as a form of portable data storage. This one stores 1 Megabyte" - var/list/files - var/list/spawn_files = list() - var/writeprotect = 0 - var/volume = 0 - var/max_volume = 1028 - - -/obj/item/weapon/disk/file/New() - ..() - icon_state = "datadisk[rand(0,6)]" - src.pixel_x = rand(-5, 5) - src.pixel_y = rand(-5, 5) - files = list() - if(istype(spawn_files)) - for(var/typekey in spawn_files) - var/datum/file/F = new typekey() - F.device = src - files += F - volume += F.volume diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 765ced8a39..42725bbe28 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -2,52 +2,6 @@ CONTAINS: Deployable items Barricades - -for reference: - access_security = 1 - access_brig = 2 - access_armory = 3 - access_forensics_lockers= 4 - access_medical = 5 - access_morgue = 6 - access_tox = 7 - access_tox_storage = 8 - access_genetics = 9 - access_engine = 10 - access_engine_equip= 11 - access_maint_tunnels = 12 - access_external_airlocks = 13 - access_emergency_storage = 14 - access_change_ids = 15 - access_ai_upload = 16 - access_teleporter = 17 - access_eva = 18 - access_heads = 19 - access_captain = 20 - access_all_personal_lockers = 21 - access_chapel_office = 22 - access_tech_storage = 23 - access_atmospherics = 24 - access_bar = 25 - access_janitor = 26 - access_crematorium = 27 - access_kitchen = 28 - access_robotics = 29 - access_rd = 30 - access_cargo = 31 - access_construction = 32 - access_chemistry = 33 - access_cargo_bot = 34 - access_hydroponics = 35 - access_manufacturing = 36 - access_library = 37 - access_lawyer = 38 - access_virology = 39 - access_cmo = 40 - access_qm = 41 - access_court = 42 - access_clown = 43 - access_mime = 44 */ //Barricades! @@ -80,6 +34,7 @@ for reference: return material /obj/structure/barricade/attackby(obj/item/W as obj, mob/user as mob) + user.setClickCooldown(user.get_attack_speed(W)) if(istype(W, /obj/item/stack)) var/obj/item/stack/D = W if(D.get_material_name() != material.name) @@ -96,37 +51,54 @@ for reference: return return else - user.setClickCooldown(user.get_attack_speed(W)) switch(W.damtype) if("fire") health -= W.force * 1 if("brute") health -= W.force * 0.75 - else - if(health <= 0) - visible_message("The barricade is smashed apart!") - dismantle() - qdel(src) - return + if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD))) + playsound(loc, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + CheckHealth() ..() +/obj/structure/barricade/proc/CheckHealth() + if(health <= 0) + dismantle() + return + +/obj/structure/barricade/take_damage(var/damage) + health -= damage + CheckHealth() + return + +/obj/structure/barricade/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + if(material == get_material_by_name("resin")) + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD))) + playsound(loc, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + health -= damage + CheckHealth() + return + /obj/structure/barricade/proc/dismantle() material.place_dismantled_product(get_turf(src)) + visible_message("\The [src] falls apart!") qdel(src) return /obj/structure/barricade/ex_act(severity) switch(severity) if(1.0) - visible_message("\The [src] is blown apart!") - qdel(src) - return + dismantle() if(2.0) health -= 25 - if(health <= 0) - visible_message("\The [src] is blown apart!") - dismantle() - return + CheckHealth() /obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. if(istype(mover) && mover.checkpass(PASSTABLE)) @@ -158,6 +130,7 @@ for reference: icon_state = "barrier[locked]" /obj/machinery/deployable/barrier/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(istype(W, /obj/item/weapon/card/id/)) if(allowed(user)) if (emagged < 2.0) @@ -196,11 +169,28 @@ for reference: health -= W.force * 0.75 if("brute") health -= W.force * 0.5 - else - if(health <= 0) - explode() + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + CheckHealth() ..() +/obj/machinery/deployable/barrier/proc/CheckHealth() + if(health <= 0) + explode() + return + +/obj/machinery/deployable/barrier/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + health -= damage + CheckHealth() + return + +/obj/machinery/deployable/barrier/take_damage(var/damage) + health -= damage + CheckHealth() + return + /obj/machinery/deployable/barrier/ex_act(severity) switch(severity) if(1.0) @@ -208,8 +198,7 @@ for reference: return if(2.0) health -= 25 - if(health <= 0) - explode() + CheckHealth() return /obj/machinery/deployable/barrier/emp_act(severity) diff --git a/code/game/machinery/doorbell_vr.dm b/code/game/machinery/doorbell_vr.dm index 25fb75df44..d6c5d67719 100644 --- a/code/game/machinery/doorbell_vr.dm +++ b/code/game/machinery/doorbell_vr.dm @@ -43,12 +43,13 @@ else icon_state = "dbchime-standby" +//TFF 3/6/19 - Port Cit RP fix of infinite frames. ToDo: Make it so that you can completely deconstruct it and reconstruct it. /obj/machinery/doorbell_chime/attackby(obj/item/W as obj, mob/user as mob) src.add_fingerprint(user) if(default_deconstruction_screwdriver(user, W)) return - else if(default_deconstruction_crowbar(user, W)) - return +// else if(default_deconstruction_crowbar(user, W)) +// return else if(default_part_replacement(user, W)) return else if(panel_open && istype(W, /obj/item/device/multitool)) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index c31c8f650b..480effcf98 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -387,7 +387,7 @@ /obj/machinery/door/airlock/uranium/process() if(world.time > last_event+20) if(prob(50)) - radiation_repository.radiate(src, rad_power) + SSradiation.radiate(src, rad_power) last_event = world.time ..() diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index ea881323c8..bf201989fe 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -56,7 +56,7 @@ icon_state = icon_state_closed else icon_state = icon_state_open - radiation_repository.resistance_cache.Remove(get_turf(src)) + SSradiation.resistance_cache.Remove(get_turf(src)) return // Has to be in here, comment at the top is older than the emag_act code on doors proper diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 0935dfa724..7e25bb2df9 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -351,6 +351,12 @@ name = "Cell 6" id = "Cell 6" + +/obj/machinery/door_timer/tactical_pet_storage //Vorestation Addition + name = "Tactical Pet Storage" + id = "tactical_pet_storage" + desc = "Opens and Closes on a timer. This one seals away a tactical boost in morale." + #undef FONT_SIZE #undef FONT_COLOR #undef FONT_STYLE diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index d276efef35..43fb8340ef 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -308,7 +308,7 @@ operating = -1 return 1 -/obj/machinery/door/proc/take_damage(var/damage) +/obj/machinery/door/take_damage(var/damage) var/initialhealth = src.health src.health = max(0, src.health - damage) if(src.health <= 0 && initialhealth > 0) @@ -382,7 +382,7 @@ icon_state = "door1" else icon_state = "door0" - radiation_repository.resistance_cache.Remove(get_turf(src)) + SSradiation.resistance_cache.Remove(get_turf(src)) return diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm index fbfbc04abf..56c8e36edf 100644 --- a/code/game/machinery/exonet_node.dm +++ b/code/game/machinery/exonet_node.dm @@ -16,6 +16,8 @@ var/list/logs = list() // Gets written to by exonet's send_message() function. +//TFF 3/6/19 - Port Cit RP fix for infinite frames + circuit = /obj/item/weapon/circuitboard/telecomms/exonet_node // Proc: New() // Parameters: None // Description: Adds components to the machine for deconstruction. @@ -23,7 +25,6 @@ ..() component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/telecomms/exonet_node(src) component_parts += new /obj/item/weapon/stock_parts/subspace/ansible(src) component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) component_parts += new /obj/item/weapon/stock_parts/manipulator(src) diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index c54734aec0..41c7a33a19 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -90,12 +90,23 @@ circuit = /obj/item/weapon/circuitboard/recharger frame_size = 3 +/datum/frame/frame_types/cell_charger + name = "Heavy-Duty Cell Charger" + frame_class = FRAME_CLASS_MACHINE + circuit = /obj/item/weapon/circuitboard/cell_charger + frame_size = 3 + /datum/frame/frame_types/grinder name = "Grinder" frame_class = FRAME_CLASS_MACHINE circuit = /obj/item/weapon/circuitboard/grinder frame_size = 3 +/datum/frame/frame_types/reagent_distillery + name = "Distillery" + frame_class = FRAME_CLASS_MACHINE + frame_size = 4 + /datum/frame/frame_types/display name = "Display" frame_class = FRAME_CLASS_DISPLAY @@ -180,13 +191,13 @@ ////////////////////////////// /obj/structure/frame - anchored = 0 + anchored = FALSE name = "frame" icon = 'icons/obj/stock_parts.dmi' icon_state = "machine_0" var/state = FRAME_PLACED var/obj/item/weapon/circuitboard/circuit = null - var/need_circuit = 1 + var/need_circuit = TRUE var/datum/frame/frame_types/frame_type = new /datum/frame/frame_types/machine var/list/components = null @@ -195,8 +206,8 @@ /obj/structure/frame/computer //used for maps frame_type = new /datum/frame/frame_types/computer - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE /obj/structure/frame/examine(mob/user) ..() @@ -248,14 +259,14 @@ pixel_y = (dir & 3)? (dir == NORTH ? -frame_type.y_offset : frame_type.y_offset) : 0 if(frame_type.circuit) - need_circuit = 0 + need_circuit = FALSE circuit = new frame_type.circuit(src) if(frame_type.name == "Computer") - density = 1 + density = TRUE if(frame_type.frame_class == FRAME_CLASS_MACHINE) - density = 1 + density = TRUE update_icon() @@ -265,7 +276,7 @@ to_chat(user, "You start to wrench the frame into place.") playsound(src.loc, P.usesound, 50, 1) if(do_after(user, 20 * P.toolspeed)) - anchored = 1 + anchored = TRUE if(!need_circuit && circuit) state = FRAME_FASTENED check_components() @@ -278,7 +289,7 @@ playsound(src, P.usesound, 50, 1) if(do_after(user, 20 * P.toolspeed)) to_chat(user, "You unfasten the frame.") - anchored = 0 + anchored = FALSE else if(istype(P, /obj/item/weapon/weldingtool)) if(state == FRAME_PLACED) @@ -575,11 +586,11 @@ set src in oview(1) if(usr.incapacitated()) - return 0 + return FALSE if(anchored) to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") - return 0 + return FALSE src.set_dir(turn(src.dir, 90)) @@ -594,11 +605,11 @@ set src in oview(1) if(usr.incapacitated()) - return 0 + return FALSE if(anchored) to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") - return 0 + return FALSE src.set_dir(turn(src.dir, 270)) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 0729121195..5d2ed9a174 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -214,7 +214,6 @@ Class Procs: /obj/machinery/CanUseTopic(var/mob/user) if(!interact_offline && (stat & (NOPOWER | BROKEN))) return STATUS_CLOSE - return ..() /obj/machinery/CouldUseTopic(var/mob/user) @@ -408,15 +407,22 @@ Class Procs: /obj/machinery/proc/dismantle() playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + //TFF 3/6/19 - port Cit RP fix of infinite frames. If it doesn't have a circuit board, don't create a frame. Return a smack instead. BONK! + if(!circuit) + return 0 var/obj/structure/frame/A = new /obj/structure/frame(src.loc) var/obj/item/weapon/circuitboard/M = circuit A.circuit = M A.anchored = 1 - A.density = 1 A.frame_type = M.board_type if(A.frame_type.circuit) A.need_circuit = 0 + if(A.frame_type.frame_class == FRAME_CLASS_ALARM || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) + A.density = 0 + else + A.density = 1 + if(A.frame_type.frame_class == FRAME_CLASS_MACHINE) for(var/obj/D in component_parts) D.forceMove(src.loc) @@ -446,3 +452,9 @@ Class Procs: M.deconstruct(src) qdel(src) return 1 + +/datum/proc/apply_visual(mob/M) + return + +/datum/proc/remove_visual(mob/M) + return diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 74cc399beb..7665fd0e83 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -4,7 +4,7 @@ /obj/machinery/oxygen_pump name = "emergency oxygen pump" icon = 'icons/obj/walllocker.dmi' - desc = "A wall mounted oxygen pump with a retractable face mask that you can pull over your face in case of emergencies." + desc = "A wall mounted oxygen pump with a retractable mask that you can pull over your face in case of emergencies." icon_state = "oxygen_tank" anchored = TRUE @@ -236,3 +236,76 @@ icon_state_closed = "anesthetic_tank" icon_state_open = "anesthetic_tank_open" mask_type = /obj/item/clothing/mask/breath/anesthetic + +/obj/machinery/oxygen_pump/mobile + name = "portable oxygen pump" + icon = 'icons/obj/atmos.dmi' + desc = "A portable oxygen pump with a retractable mask that you can pull over your face in case of emergencies." + icon_state = "medpump" + icon_state_open = "medpump_open" + icon_state_closed = "medpump" + + anchored = FALSE + density = TRUE + + mask_type = /obj/item/clothing/mask/gas/clear + + var/last_area = null + +/obj/machinery/oxygen_pump/mobile/process() + ..() + + var/turf/T = get_turf(src) + + if(!last_area && T) + last_area = T.loc + + if(last_area != T.loc) + power_change() + last_area = T.loc + +/obj/machinery/oxygen_pump/mobile/anesthetic + name = "portable anesthetic pump" + spawn_type = /obj/item/weapon/tank/anesthetic + icon_state = "medpump_n2o" + icon_state_closed = "medpump_n2o" + icon_state_open = "medpump_n2o_open" + mask_type = /obj/item/clothing/mask/breath/anesthetic + +/obj/machinery/oxygen_pump/mobile/stabilizer + name = "portable patient stabilizer" + desc = "A portable oxygen pump with a retractable mask used for stabilizing patients in the field." + +/obj/machinery/oxygen_pump/mobile/stabilizer/process() + if(breather) + if(!can_apply_to_target(breather)) + if(tank) + tank.forceMove(src) + breather.remove_from_mob(contained) + contained.forceMove(src) + src.visible_message("\The [contained] rapidly retracts back into \the [src]!") + breather = null + use_power = 1 + else if(!breather.internal && tank) + breather.internal = tank + if(breather.internals) + breather.internals.icon_state = "internal0" + + if(breather) // Safety. + if(ishuman(breather)) + var/mob/living/carbon/human/H = breather + + if(H.stat == DEAD) + H.add_modifier(/datum/modifier/bloodpump_corpse, 6 SECONDS) + + else + H.add_modifier(/datum/modifier/bloodpump, 6 SECONDS) + + var/turf/T = get_turf(src) + + if(!last_area && T) + last_area = T.loc + + if(last_area != T.loc) + power_change() + last_area = T.loc diff --git a/code/game/machinery/pipe/pipe_recipes.dm b/code/game/machinery/pipe/pipe_recipes.dm index f0ae483055..a54cdd4eee 100644 --- a/code/game/machinery/pipe/pipe_recipes.dm +++ b/code/game/machinery/pipe/pipe_recipes.dm @@ -27,6 +27,7 @@ var/global/list/atmos_pipe_recipes = null new /datum/pipe_recipe/pipe("Gas Pump", /obj/machinery/atmospherics/binary/pump), new /datum/pipe_recipe/pipe("Pressure Regulator", /obj/machinery/atmospherics/binary/passive_gate), new /datum/pipe_recipe/pipe("High Power Gas Pump",/obj/machinery/atmospherics/binary/pump/high_power), + //new /datum/pipe_recipe/pipe("Automatic Shutoff Valve",/obj/machinery/atmospherics/valve/shutoff), //VOREStation Removal: Without leaks, those are just regular valves, new /datum/pipe_recipe/pipe("Scrubber", /obj/machinery/atmospherics/unary/vent_scrubber), new /datum/pipe_recipe/meter("Meter"), new /datum/pipe_recipe/pipe("Gas Filter", /obj/machinery/atmospherics/trinary/atmos_filter), diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 948ef595c8..9c29365ae4 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -36,83 +36,75 @@ name = "turret" catalogue_data = list(/datum/category_item/catalogue/technology/turret) icon = 'icons/obj/turrets.dmi' - icon_state = "turret_cover" - anchored = 1 + icon_state = "turret_cover_normal" + anchored = TRUE - density = 0 - use_power = 1 //this turret uses and requires power + density = FALSE + use_power = TRUE //this turret uses and requires power idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power power_channel = EQUIP //drains power from the EQUIPMENT channel req_one_access = list(access_security, access_heads) - // icon_states for turrets. - // These are for the turret covers. - var/closed_state = "turret_cover" // For when it is closed. - var/raising_state = "popup" // When turret is opening. - var/opened_state = "open" // When fully opened. - var/lowering_state = "popdown" // When closing. - var/gun_active_state = "target_prism" // The actual gun's icon_state when active. - var/gun_disabled_state = "grey_target_prism" // Gun sprite when depowered/disabled. - var/gun_destroyed_state = "destroyed_target_prism" // Turret sprite for when the turret dies. - - var/raised = 0 //if the turret cover is "open" and the turret is raised - var/raising= 0 //if the turret is currently opening or closing its cover - var/health = 80 //the turret's health - var/maxhealth = 80 //turrets maximal health. - var/auto_repair = 0 //if 1 the turret slowly repairs itself. - var/locked = 1 //if the turret's behaviour control access is locked - var/controllock = 0 //if the turret responds to control panels + var/raised = FALSE //if the turret cover is "open" and the turret is raised + var/raising= FALSE //if the turret is currently opening or closing its cover + var/health = 80 //the turret's health + var/maxhealth = 80 //turrets maximal health. + var/auto_repair = FALSE //if 1 the turret slowly repairs itself. + var/locked = TRUE //if the turret's behaviour control access is locked + var/controllock = FALSE //if the turret responds to control panels var/installation = /obj/item/weapon/gun/energy/gun //the type of weapon installed - var/gun_charge = 0 //the charge of the gun inserted - var/projectile = null //holder for bullettype - var/eprojectile = null //holder for the shot when emagged - var/reqpower = 500 //holder for power needed - var/iconholder = null //holder for the icon_state. 1 for sprite based on icon_color, null for blue. - var/icon_color = "orange" // When iconholder is set to 1, the icon_state changes based on what is in this variable. - var/egun = null //holder to handle certain guns switching bullettypes + var/gun_charge = 0 //the charge of the gun inserted + var/projectile = null //holder for bullettype + var/lethal_projectile = null //holder for the shot when emagged + var/reqpower = 500 //holder for power needed + var/turret_type = "normal" + var/icon_color = "blue" + var/lethal_icon_color = "blue" - var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire - var/shot_delay = 15 //1.5 seconds between each shot + var/last_fired = FALSE //TRUE: if the turret is cooling down from a shot, FALSE: turret is ready to fire + var/shot_delay = 1.5 SECONDS //1.5 seconds between each shot - var/check_arrest = 1 //checks if the perp is set to arrest - var/check_records = 1 //checks if a security record exists at all - var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg - var/check_all = 0 //If active, will fire on anything, including synthetics. - var/ailock = 0 // AI cannot use this - var/faction = null //if set, will not fire at people in the same faction for any reason. + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_all = FALSE //If active, will fire on anything, including synthetics. + var/ailock = FALSE // AI cannot use this + var/check_down = FALSE //If active, will shoot to kill when lethals are also on + var/faction = null //if set, will not fire at people in the same faction for any reason. - var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!) + var/attacked = FALSE //if set to TRUE, the turret gets pissed off and shoots at people nearby (unless they have sec access!) - var/enabled = 1 //determines if the turret is on - var/lethal = 0 //whether in lethal or stun mode - var/disabled = 0 + var/enabled = TRUE //determines if the turret is on + var/lethal = FALSE //whether in lethal or stun mode + var/disabled = FALSE - var/shot_sound //what sound should play when the turret fires - var/eshot_sound //what sound should play when the emagged turret fires + var/shot_sound //what sound should play when the turret fires + var/lethal_shot_sound //what sound should play when the emagged turret fires var/datum/effect/effect/system/spark_spread/spark_system //the spark system, used for generating... sparks? - var/wrenching = 0 + var/wrenching = FALSE var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down. var/can_salvage = TRUE // If false, salvaging doesn't give you anything. /obj/machinery/porta_turret/crescent req_one_access = list(access_cent_specops) - enabled = 0 - ailock = 1 - check_synth = 0 - check_access = 1 - check_arrest = 1 - check_records = 1 - check_weapons = 1 - check_anomalies = 1 - check_all = 0 + enabled = FALSE + ailock = TRUE + check_synth = FALSE + check_access = TRUE + check_arrest = TRUE + check_records = TRUE + check_weapons = TRUE + check_anomalies = TRUE + check_all = FALSE + check_down = TRUE /obj/machinery/porta_turret/can_catalogue(mob/user) // Dead turrets can't be scanned. if(stat & BROKEN) @@ -121,8 +113,8 @@ return ..() /obj/machinery/porta_turret/stationary - ailock = 1 - lethal = 1 + ailock = TRUE + lethal = TRUE installation = /obj/item/weapon/gun/energy/laser /obj/machinery/porta_turret/stationary/syndie // Generic turrets for POIs that need to not shoot their buddies. @@ -139,7 +131,6 @@ health = 250 // Since lasers do 40 each. maxhealth = 250 - /datum/category_item/catalogue/anomalous/precursor_a/alien_turret name = "Precursor Alpha Object - Turrets" desc = "An autonomous defense turret created by unknown ancient aliens. It utilizes an \ @@ -156,7 +147,7 @@ name = "interior anti-boarding turret" desc = "A very tough looking turret made by alien hands." catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_turret) - icon_state = "alien_turret_cover" + icon_state = "turret_cover_alien" req_one_access = list(access_alien) installation = /obj/item/weapon/gun/energy/alien enabled = TRUE @@ -165,22 +156,51 @@ check_all = TRUE health = 250 // Similar to the AI turrets. maxhealth = 250 - - closed_state = "alien_turret_cover" - raising_state = "alien_popup" - opened_state = "alien_open" - lowering_state = "alien_popdown" - gun_active_state = "alien_gun" - gun_disabled_state = "alien_gun_disabled" - gun_destroyed_state = "alien_gun_destroyed" + turret_type = "alien" /obj/machinery/porta_turret/alien/destroyed // Turrets that are already dead, to act as a warning of what the rest of the submap contains. name = "broken interior anti-boarding turret" desc = "A very tough looking turret made by alien hands. This one looks destroyed, thankfully." - icon_state = "alien_gun_destroyed" + icon_state = "destroyed_target_prism_alien" stat = BROKEN can_salvage = FALSE // So you need to actually kill a turret to get the alien gun. +/obj/machinery/porta_turret/industrial + name = "industrial turret" + desc = "This variant appears to be much more rugged." + req_one_access = list(access_heads) + icon_state = "turret_cover_industrial" + installation = /obj/item/weapon/gun/energy/phasegun + health = 200 + maxhealth = 200 + turret_type = "industrial" + +/obj/machinery/porta_turret/industrial/bullet_act(obj/item/projectile/Proj) + var/damage = round(Proj.get_structure_damage() * 1.33) + + if(!damage) + return + + if(enabled) + if(!attacked && !emagged) + attacked = TRUE + spawn() + sleep(60) + attacked = FALSE + + take_damage(damage) + +/obj/machinery/porta_turret/industrial/attack_generic(mob/living/L, damage) + return ..(L, damage * 0.8) + +/obj/machinery/porta_turret/industrial/teleport_defense + name = "defense turret" + desc = "This variant appears to be much more durable, with a rugged outer coating." + req_one_access = list(access_heads) + installation = /obj/item/weapon/gun/energy/gun/burst + health = 250 + maxhealth = 250 + /obj/machinery/porta_turret/poi //These are always angry enabled = TRUE lethal = TRUE @@ -188,6 +208,98 @@ check_all = TRUE can_salvage = FALSE // So you can't just twoshot a turret and get a fancy gun +/obj/machinery/porta_turret/lasertag + name = "lasertag turret" + turret_type = "normal" + req_one_access = list() + installation = /obj/item/weapon/gun/energy/lasertag/omni + + locked = FALSE + enabled = FALSE + anchored = FALSE + //These two are used for lasertag + check_synth = FALSE + check_weapons = FALSE + //These vars aren't used + check_access = FALSE + check_arrest = FALSE + check_records = FALSE + check_anomalies = FALSE + check_all = FALSE + check_down = FALSE + +/obj/machinery/porta_turret/lasertag/red + turret_type = "red" + installation = /obj/item/weapon/gun/energy/lasertag/red + check_weapons = TRUE // Used to target blue players + +/obj/machinery/porta_turret/lasertag/blue + turret_type = "blue" + installation = /obj/item/weapon/gun/energy/lasertag/blue + check_synth = TRUE // Used to target red players + +/obj/machinery/porta_turret/lasertag/assess_living(var/mob/living/L) + if(!ishuman(L)) + return TURRET_NOT_TARGET + + if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var + return TURRET_NOT_TARGET + + if(get_dist(src, L) > 7) //if it's too far away, why bother? + return TURRET_NOT_TARGET + + if(!(L in check_trajectory(L, src))) //check if we have true line of sight + return TURRET_NOT_TARGET + + if(L.lying) //Don't need to stun-lock the players + return TURRET_NOT_TARGET + + if(ishuman(L)) + var/mob/living/carbon/human/M = L + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag) && check_synth) // Checks if they are a red player + return TURRET_PRIORITY_TARGET + + if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag) && check_weapons) // Checks if they are a blue player + return TURRET_PRIORITY_TARGET + +/obj/machinery/porta_turret/lasertag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + data["access"] = !isLocked(user) + data["locked"] = locked + data["enabled"] = enabled + //data["is_lethal"] = 1 // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. + //data["lethal"] = lethal // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. + + if(data["access"]) + var/settings[0] + settings[++settings.len] = list("category" = "Target Red", "setting" = "check_synth", "value" = check_synth) // Could not get the UI to work with new vars specifically for lasertag turrets -Nalarac + settings[++settings.len] = list("category" = "Target Blue", "setting" = "check_weapons", "value" = check_weapons) // So I'm using these variables since they don't do anything else in this case + data["settings"] = settings + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/porta_turret/lasertag/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["command"] && href_list["value"]) + var/value = text2num(href_list["value"]) + if(href_list["command"] == "enable") + enabled = value + //else if(href_list["command"] == "lethal") // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. + //lethal = value // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. + else if(href_list["command"] == "check_synth") + check_synth = value + else if(href_list["command"] == "check_weapons") + check_weapons = value + + return 1 + /obj/machinery/porta_turret/Initialize() //Sets up a spark system spark_system = new /datum/effect/effect/system/spark_spread @@ -197,7 +309,7 @@ setup() // If turrets ever switch overlays, this will need to be cached and reapplied each time overlays_cut() is called. - var/image/turret_opened_overlay = image(icon, opened_state) + var/image/turret_opened_overlay = image(icon, "open_[turret_type]") turret_opened_overlay.layer = layer-0.1 add_overlay(turret_opened_overlay) return ..() @@ -209,81 +321,85 @@ /obj/machinery/porta_turret/update_icon() if(stat & BROKEN) // Turret is dead. - icon_state = gun_destroyed_state + icon_state = "destroyed_target_prism_[turret_type]" else if(raised || raising) // Turret is open. if(powered() && enabled) // Trying to shoot someone. - icon_state = gun_active_state + if(lethal) + icon_state = "[lethal_icon_color]_target_prism_[turret_type]" + else + icon_state = "[icon_color]_target_prism_[turret_type]" else // Disabled. - icon_state = gun_disabled_state + icon_state = "grey_target_prism_[turret_type]" else // Its closed. - icon_state = closed_state + icon_state = "turret_cover_[turret_type]" /obj/machinery/porta_turret/proc/setup() var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable + var/obj/item/projectile/P = initial(E.projectile_type) //var/obj/item/ammo_casing/shottype = E.projectile_type - projectile = initial(E.projectile_type) - eprojectile = projectile - shot_sound = initial(E.fire_sound) - eshot_sound = shot_sound + projectile = P + lethal_projectile = projectile + shot_sound = initial(P.fire_sound) + lethal_shot_sound = shot_sound + + if(istype(P, /obj/item/projectile/energy)) + icon_color = "orange" + + else if(istype(P, /obj/item/projectile/beam/stun)) + icon_color = "blue" + + else if(istype(P, /obj/item/projectile/beam/lasertag)) + icon_color = "blue" + + else if(istype(P, /obj/item/projectile/beam)) + icon_color = "red" + + else + icon_color = "blue" + + lethal_icon_color = icon_color weapon_setup(installation) /obj/machinery/porta_turret/proc/weapon_setup(var/guntype) switch(guntype) - if(/obj/item/weapon/gun/energy/laser/practice) - iconholder = 1 - eprojectile = /obj/item/projectile/beam + if(/obj/item/weapon/gun/energy/gun/burst) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam/burstlaser + lethal_shot_sound = 'sound/weapons/Laser.ogg' + shot_delay = 1 SECOND -// if(/obj/item/weapon/gun/energy/laser/practice/sc_laser) -// iconholder = 1 -// eprojectile = /obj/item/projectile/beam - - if(/obj/item/weapon/gun/energy/retro) - iconholder = 1 - -// if(/obj/item/weapon/gun/energy/retro/sc_retro) -// iconholder = 1 - - if(/obj/item/weapon/gun/energy/captain) - iconholder = 1 - - if(/obj/item/weapon/gun/energy/lasercannon) - iconholder = 1 - - if(/obj/item/weapon/gun/energy/taser) - eprojectile = /obj/item/projectile/beam - eshot_sound = 'sound/weapons/Laser.ogg' - - if(/obj/item/weapon/gun/energy/stunrevolver) - eprojectile = /obj/item/projectile/beam - eshot_sound = 'sound/weapons/Laser.ogg' + if(/obj/item/weapon/gun/energy/phasegun) + icon_color = "orange" + lethal_icon_color = "orange" + lethal_projectile = /obj/item/projectile/energy/phase/heavy + shot_delay = 1 SECOND if(/obj/item/weapon/gun/energy/gun) - eprojectile = /obj/item/projectile/beam //If it has, going to kill mode - eshot_sound = 'sound/weapons/Laser.ogg' - egun = 1 + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode + lethal_shot_sound = 'sound/weapons/Laser.ogg' if(/obj/item/weapon/gun/energy/gun/nuclear) - eprojectile = /obj/item/projectile/beam //If it has, going to kill mode - eshot_sound = 'sound/weapons/Laser.ogg' - egun = 1 + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode + lethal_shot_sound = 'sound/weapons/Laser.ogg' if(/obj/item/weapon/gun/energy/xray) - eprojectile = /obj/item/projectile/beam/xray + lethal_icon_color = "green" + lethal_projectile = /obj/item/projectile/beam/xray projectile = /obj/item/projectile/beam/stun // Otherwise we fire xrays on both modes. - eshot_sound = 'sound/weapons/eluger.ogg' + lethal_shot_sound = 'sound/weapons/eluger.ogg' shot_sound = 'sound/weapons/Taser.ogg' - iconholder = 1 - icon_color = "green" /obj/machinery/porta_turret/proc/isLocked(mob/user) if(ailock && issilicon(user)) @@ -291,7 +407,7 @@ return 1 if(locked && !issilicon(user)) - to_chat(user, "Access denied.") + to_chat(user, "Controls locked.") return 1 return 0 @@ -325,6 +441,7 @@ settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) settings[++settings.len] = list("category" = "Check misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) settings[++settings.len] = list("category" = "Neutralize All Entities", "setting" = "check_all", "value" = check_all) + settings[++settings.len] = list("category" = "Neutralize Downed Entities", "setting" = "check_down", "value" = check_down) data["settings"] = settings ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -376,6 +493,8 @@ check_anomalies = value else if(href_list["command"] == "check_all") check_all = value + else if(href_list["command"] == "check_down") + check_down = value return 1 @@ -426,20 +545,20 @@ "You begin [anchored ? "un" : ""]securing the turret." \ ) - wrenching = 1 + wrenching = TRUE if(do_after(user, 50 * I.toolspeed)) //This code handles moving the turret around. After all, it's a portable turret! if(!anchored) playsound(loc, I.usesound, 100, 1) - anchored = 1 + anchored = TRUE update_icon() to_chat(user, "You secure the exterior bolts on the turret.") else if(anchored) playsound(loc, I.usesound, 100, 1) - anchored = 0 + anchored = FALSE to_chat(user, "You unsecure the exterior bolts on the turret.") update_icon() - wrenching = 0 + wrenching = FALSE else if(istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) //Behavior lock/unlock mangement @@ -480,15 +599,14 @@ //the turret shoot much, much faster. to_chat(user, "You short out [src]'s threat assessment circuits.") visible_message("[src] hums oddly...") - emagged = 1 - iconholder = 1 - controllock = 1 - enabled = 0 //turns off the turret temporarily + emagged = TRUE + controllock = TRUE + enabled = FALSE //turns off the turret temporarily sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - enabled = 1 //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here + enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here return 1 -/obj/machinery/porta_turret/proc/take_damage(var/force) +/obj/machinery/porta_turret/take_damage(var/force) if(!raised && !raising) force = force / 8 if(force < 5) @@ -511,7 +629,7 @@ attacked = 1 spawn() sleep(60) - attacked = 0 + attacked = FALSE ..() @@ -527,12 +645,12 @@ check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on check_anomalies = prob(50) if(prob(5)) - emagged = 1 + emagged = TRUE enabled=0 spawn(rand(60,600)) if(!enabled) - enabled=1 + enabled = TRUE ..() @@ -617,10 +735,10 @@ if(faction && L.faction == faction) return TURRET_NOT_TARGET - if(!emagged && issilicon(L) && check_all == 0) // Don't target silica, unless told to neutralize everything. + if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. return TURRET_NOT_TARGET - if(L.stat && !emagged) //if the perp is dead/dying, no need to bother really + if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really return TURRET_NOT_TARGET //move onto next potential victim! if(get_dist(src, L) > 7) //if it's too far away, why bother? @@ -637,13 +755,13 @@ if(check_synth || check_all) //If it's set to attack all non-silicons or everything, target them! if(L.lying) - return lethal ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET return TURRET_PRIORITY_TARGET if(iscuffed(L)) // If the target is handcuffed, leave it alone return TURRET_NOT_TARGET - if(isanimal(L) || issmall(L)) // Animals are not so dangerous + if(isanimal(L)) // Animals are not so dangerous return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET if(isxenomorph(L) || isalien(L)) // Xenos are dangerous @@ -654,7 +772,7 @@ return TURRET_NOT_TARGET //if threat level < 4, keep going if(L.lying) //if the perp is lying down, it's still a target but a less-important target - return lethal ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee @@ -690,7 +808,7 @@ var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) flick_holder.layer = layer + 0.1 - flick(raising_state, flick_holder) + flick("popup_[turret_type]", flick_holder) sleep(10) qdel(flick_holder) @@ -711,7 +829,7 @@ var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) flick_holder.layer = layer + 0.1 - flick(lowering_state, flick_holder) + flick("popdown_[turret_type]", flick_holder) sleep(10) qdel(flick_holder) @@ -742,10 +860,10 @@ if(!(emagged || attacked)) //if it hasn't been emagged or attacked, it has to obey a cooldown rate if(last_fired || !raised) //prevents rapid-fire shooting, unless it's been emagged return - last_fired = 1 + last_fired = TRUE spawn() sleep(shot_delay) - last_fired = 0 + last_fired = FALSE var/turf/T = get_turf(src) var/turf/U = get_turf(target) @@ -758,8 +876,8 @@ update_icon() var/obj/item/projectile/A if(emagged || lethal) - A = new eprojectile(loc) - playsound(loc, eshot_sound, 75, 1) + A = new lethal_projectile(loc) + playsound(loc, lethal_shot_sound, 75, 1) else A = new projectile(loc) playsound(loc, shot_sound, 75, 1) @@ -780,8 +898,7 @@ //Shooting Code: A.firer = src A.old_style_target(target) - A.def_zone = def_zone - A.fire() + A.launch_projectile_from_turf(target, def_zone, src) // Reset the time needed to go back down, since we just tried to shoot at someone. timeout = 10 @@ -803,7 +920,6 @@ return enabled = TC.enabled lethal = TC.lethal - iconholder = TC.lethal check_synth = TC.check_synth check_access = TC.check_access @@ -839,7 +955,7 @@ if(I.is_wrench() && !anchored) playsound(loc, I.usesound, 100, 1) to_chat(user, "You secure the external bolts.") - anchored = 1 + anchored = TRUE build_step = 1 return @@ -864,7 +980,7 @@ else if(I.is_wrench()) playsound(loc, I.usesound, 75, 1) to_chat(user, "You unfasten the external bolts.") - anchored = 0 + anchored = FALSE build_step = 0 return @@ -1022,4 +1138,4 @@ #undef TURRET_PRIORITY_TARGET #undef TURRET_SECONDARY_TARGET -#undef TURRET_NOT_TARGET \ No newline at end of file +#undef TURRET_NOT_TARGET diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 0b985d3047..e999742507 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -1,14 +1,16 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -obj/machinery/recharger +/obj/machinery/recharger name = "recharger" + desc = "A standard recharger for all devices that use power." icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit icon_state = "recharger0" anchored = 1 use_power = 1 idle_power_usage = 4 active_power_usage = 40000 //40 kW + var/efficiency = 40000 //will provide the modified power rate when upgraded var/obj/item/charging = null - var/list/allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton, /obj/item/device/laptop, /obj/item/weapon/cell, /obj/item/device/flashlight, /obj/item/device/electronic_assembly, /obj/item/weapon/weldingtool/electric, /obj/item/ammo_magazine/smart, /obj/item/device/flash, /obj/item/ammo_casing/nsfw_batt) //VOREStation Add - NSFW Batteries + var/list/allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton, /obj/item/modular_computer, /obj/item/weapon/computer_hardware/battery_module, /obj/item/weapon/cell, /obj/item/device/flashlight, /obj/item/device/electronic_assembly, /obj/item/weapon/weldingtool/electric, /obj/item/ammo_magazine/smart, /obj/item/device/flash, /obj/item/ammo_casing/microbattery) //VOREStation Add - NSFW Batteries var/icon_state_charged = "recharger2" var/icon_state_charging = "recharger1" var/icon_state_idle = "recharger0" //also when unpowered @@ -23,10 +25,16 @@ obj/machinery/recharger ..() return -/obj/machinery/recharger/attackby(obj/item/weapon/G as obj, mob/user as mob) - if(istype(user,/mob/living/silicon)) +/obj/machinery/recharger/examine(mob/user) + if(!..(user, 5)) return + to_chat(user, "[charging ? "[charging]" : "Nothing"] is in [src].") + if(charging) + var/obj/item/weapon/cell/C = charging.get_cell() + to_chat(user, "Current charge: [C.charge] / [C.maxcharge]") + +/obj/machinery/recharger/attackby(obj/item/weapon/G as obj, mob/user as mob) var/allowed = 0 for (var/allowed_type in allowed_devices) if(istype(G, allowed_type)) allowed = 1 @@ -37,45 +45,76 @@ obj/machinery/recharger return // Checks to make sure he's not in space doing it, and that the area got proper power. if(!powered()) - to_chat(user, "The [name] blinks red as you try to insert the item!") + to_chat(user, "\The [src] blinks red as you try to insert [G]!") return if(istype(G, /obj/item/weapon/gun/energy)) var/obj/item/weapon/gun/energy/E = G if(E.self_recharge) - to_chat(user, "Your gun has no recharge port.") + to_chat(user, "\The [E] has no recharge port.") return - if(!G.get_cell() && !istype(G, /obj/item/ammo_casing/nsfw_batt)) //VOREStation Edit: NSFW charging - to_chat(user, "This device does not have a battery installed.") + if(istype(G, /obj/item/modular_computer)) + var/obj/item/modular_computer/C = G + if(!C.battery_module) + to_chat(user, "\The [C] does not have a battery installed. ") + return + if(istype(G, /obj/item/weapon/melee/baton)) + var/obj/item/weapon/melee/baton/B = G + if(B.use_external_power) + to_chat(user, "\The [B] has no recharge port.") + return + if(istype(G, /obj/item/device/flash)) + var/obj/item/device/flash/F = G + if(F.use_external_power) + to_chat(user, "\The [F] has no recharge port.") + return + if(istype(G, /obj/item/weapon/weldingtool/electric)) + var/obj/item/weapon/weldingtool/electric/EW = G + if(EW.use_external_power) + to_chat(user, "\The [EW] has no recharge port.") + return + else if(!G.get_cell() && !istype(G, /obj/item/ammo_casing/microbattery)) //VOREStation Edit: NSFW charging + to_chat(user, "\The [G] does not have a battery installed.") return user.drop_item() G.loc = src charging = G update_icon() + user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") + else if(portable && G.is_wrench()) if(charging) to_chat(user, "Remove [charging] first!") return anchored = !anchored - to_chat(user, "You [anchored ? "attached" : "detached"] the recharger.") + to_chat(user, "You [anchored ? "attached" : "detached"] [src].") playsound(loc, G.usesound, 75, 1) else if(default_deconstruction_screwdriver(user, G)) return else if(default_deconstruction_crowbar(user, G)) return - -/obj/machinery/recharger/attack_hand(mob/user as mob) - if(istype(user,/mob/living/silicon)) + else if(default_part_replacement(user, G)) return +/obj/machinery/recharger/attack_hand(mob/user as mob) add_fingerprint(user) if(charging) + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") charging.update_icon() user.put_in_hands(charging) charging = null update_icon() +/obj/machinery/cell_charger/attack_ai(mob/user) + if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Borgs can remove the cell if they are near enough + if(charging) + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + charging.update_icon() + charging.loc = src.loc + charging = null + update_icon() + /obj/machinery/recharger/process() if(stat & (NOPOWER|BROKEN) || !anchored) update_use_power(0) @@ -86,19 +125,40 @@ obj/machinery/recharger update_use_power(1) icon_state = icon_state_idle else + if(istype(charging, /obj/item/modular_computer)) + var/obj/item/modular_computer/C = charging + if(!C.battery_module.battery.fully_charged()) + icon_state = icon_state_charging + C.battery_module.battery.give(CELLRATE*efficiency) + update_use_power(2) + else + icon_state = icon_state_charged + update_use_power(1) + return + else if(istype(charging, /obj/item/weapon/computer_hardware/battery_module)) + var/obj/item/weapon/computer_hardware/battery_module/BM = charging + if(!BM.battery.fully_charged()) + icon_state = icon_state_charging + BM.battery.give(CELLRATE*efficiency) + update_use_power(2) + else + icon_state = icon_state_charged + update_use_power(1) + return + var/obj/item/weapon/cell/C = charging.get_cell() if(istype(C)) if(!C.fully_charged()) icon_state = icon_state_charging - C.give(active_power_usage*CELLRATE) + C.give(CELLRATE*efficiency) update_use_power(2) else icon_state = icon_state_charged update_use_power(1) //VOREStation Add - NSFW Batteries - else if(istype(charging, /obj/item/ammo_casing/nsfw_batt)) - var/obj/item/ammo_casing/nsfw_batt/batt = charging + else if(istype(charging, /obj/item/ammo_casing/microbattery)) + var/obj/item/ammo_casing/microbattery/batt = charging if(batt.shots_left >= initial(batt.shots_left)) icon_state = icon_state_charged update_use_power(1) @@ -127,16 +187,24 @@ obj/machinery/recharger else icon_state = icon_state_idle +/obj/machinery/recharger/RefreshParts() + var/E = 0 + for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) + E += C.rating + efficiency = active_power_usage * (1+ (E - 1)*0.5) + /obj/machinery/recharger/wallcharger name = "wall recharger" + desc = "A more powerful recharger designed for energy weapons." icon = 'icons/obj/stationobjs.dmi' icon_state = "wrecharger0" plane = TURF_PLANE layer = ABOVE_TURF_LAYER - active_power_usage = 25000 //25 kW , It's more specialized than the standalone recharger (guns, batons, and flashlights only) so make it more powerful + active_power_usage = 60000 //60 kW , It's more specialized than the standalone recharger (guns, batons, and flashlights only) so make it more powerful + efficiency = 60000 allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/gun/magnetic, /obj/item/weapon/melee/baton, /obj/item/device/flashlight, /obj/item/weapon/cell/device) icon_state_charged = "wrecharger2" icon_state_charging = "wrecharger1" icon_state_idle = "wrecharger0" portable = 0 - circuit = /obj/item/weapon/circuitboard/recharger/wrecharger \ No newline at end of file + circuit = /obj/item/weapon/circuitboard/recharger/wrecharger diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index d94ac049b9..7939514beb 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -648,7 +648,7 @@ model_text = "Exploration" departments = list("Exploration","Old Exploration") -/obj/machinery/suit_cycler/exploreration/Initialize() +/obj/machinery/suit_cycler/exploration/Initialize() species -= SPECIES_TESHARI return ..() @@ -931,9 +931,10 @@ /obj/machinery/suit_cycler/proc/finished_job() var/turf/T = get_turf(src) - T.visible_message("\icon[src]The [src] pings loudly.") + T.audible_message("\icon[src]The [src] beeps several times.") icon_state = initial(icon_state) active = 0 + playsound(src, 'sound/machines/boobeebeep.ogg', 50) updateUsrDialog() /obj/machinery/suit_cycler/proc/repair_suit() diff --git a/code/game/machinery/suit_storage_unit_vr.dm b/code/game/machinery/suit_storage_unit_vr.dm index f1b01d9a3c..504a71c2a5 100644 --- a/code/game/machinery/suit_storage_unit_vr.dm +++ b/code/game/machinery/suit_storage_unit_vr.dm @@ -23,7 +23,5 @@ req_access = list(access_explorer) departments = list("Exploration") -// Pilot Blue is still missing a few sprites on polaris end /obj/machinery/suit_cycler/pilot req_access = list(access_pilot) - departments = list("Pilot") diff --git a/code/game/machinery/syndicatebeacon_vr.dm b/code/game/machinery/syndicatebeacon_vr.dm new file mode 100644 index 0000000000..ef6b3da0bb --- /dev/null +++ b/code/game/machinery/syndicatebeacon_vr.dm @@ -0,0 +1,40 @@ +// Virgo modified syndie beacon, does not give objectives + +/obj/machinery/syndicate_beacon/virgo/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
    Identity confirmed,
    " + if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) + if(is_special_character(user)) + dat += "Operative record found. Greetings, Agent [user.name].
    " + else if(charges < 1) + dat += "Connection severed.
    " + else + var/honorific = "Mr." + if(user.gender == FEMALE) + honorific = "Ms." + dat += "Identity not found in operative database. What can the Black Market do for you today, [honorific] [user.name]?
    " + if(!selfdestructing) + dat += "

    \"[pick("Send me some supplies!", "Transfer supplies.")]\"
    " + dat += temptext + user << browse(dat, "window=syndbeacon") + onclose(user, "syndbeacon") + +/obj/machinery/syndicate_beacon/virgo/Topic(href, href_list) + if(href_list["betraitor"]) + if(charges < 1) + updateUsrDialog() + return + var/mob/M = locate(href_list["traitormob"]) + if(M.mind.special_role || jobban_isbanned(M, "Syndicate")) + temptext = "We have no need for you at this time. Have a pleasant day.
    " + updateUsrDialog() + return + charges -= 1 + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/N = M + to_chat(N, "Access granted, here are the supplies!") + traitors.equip(N) + message_admins("[N]/([N.ckey]) has recieved an uplink and telecrystals from the syndicate beacon.") + + updateUsrDialog() + return diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index cd65fb9f91..8f6e8e589e 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -22,7 +22,17 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept machinetype = 5 produces_heat = 0 delay = 7 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/broadcaster" + circuit = /obj/item/weapon/circuitboard/telecomms/broadcaster + +/obj/machinery/telecomms/processor/Initialize() + . = ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/crystal(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser/high(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) /obj/machinery/telecomms/broadcaster/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) // Don't broadcast rejected signals diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index 331f5644d9..d41104df25 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/machine_interactions.dm @@ -12,7 +12,6 @@ /obj/machinery/telecomms var/temp = "" // output message - var/construct_op = 0 /obj/machinery/telecomms/attackby(obj/item/P as obj, mob/user as mob) @@ -21,7 +20,6 @@ if(istype(P, /obj/item/device/multitool)) attack_hand(user) - // REPAIRING: Use Nanopaste to repair 10-20 integrity points. if(istype(P, /obj/item/stack/nanopaste)) var/obj/item/stack/nanopaste/T = P @@ -34,75 +32,10 @@ return - switch(construct_op) - if(0) - if(P.is_screwdriver()) - to_chat(user, "You unfasten the bolts.") - playsound(src.loc, P.usesound, 50, 1) - construct_op ++ - if(1) - if(P.is_screwdriver()) - to_chat(user, "You fasten the bolts.") - playsound(src.loc, P.usesound, 50, 1) - construct_op -- - if(P.is_wrench()) - to_chat(user, "You dislodge the external plating.") - playsound(src.loc, P.usesound, 75, 1) - construct_op ++ - if(2) - if(P.is_wrench()) - to_chat(user, "You secure the external plating.") - playsound(src.loc, P.usesound, 75, 1) - construct_op -- - if(P.is_wirecutter()) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - construct_op ++ - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( user.loc ) - A.amount = 5 - stat |= BROKEN // the machine's been borked! - if(3) - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/A = P - if (A.use(5)) - to_chat(user, "You insert the cables.") - construct_op-- - stat &= ~BROKEN // the machine's not borked anymore! - else - to_chat(user, "You need five coils of wire for this.") - if(P.is_crowbar()) - to_chat(user, "You begin prying out the circuit board other components...") - playsound(src.loc, P.usesound, 50, 1) - if(do_after(user,60 * P.toolspeed)) - to_chat(user, "You finish prying out the components.") - - // Drop all the component stuff - if(contents.len > 0) - for(var/obj/x in src) - x.loc = user.loc - else - - // If the machine wasn't made during runtime, probably doesn't have components: - // manually find the components and drop them! - var/newpath = text2path(circuitboard) - var/obj/item/weapon/circuitboard/C = new newpath - for(var/I in C.req_components) - for(var/i = 1, i <= C.req_components[I], i++) - newpath = text2path(I) - var/obj/item/s = new newpath - s.loc = user.loc - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/A = P - A.amount = 1 - - // Drop a circuit board too - C.loc = user.loc - - // Create a frame and delete the current machine - var/obj/structure/frame/F = new - F.loc = src.loc - qdel(src) - + if(default_deconstruction_screwdriver(user, P)) + return + if(default_deconstruction_crowbar(user, P)) + return /obj/machinery/telecomms/attack_ai(var/mob/user as mob) attack_hand(user) diff --git a/code/game/machinery/telecomms/presets_vr.dm b/code/game/machinery/telecomms/presets_vr.dm index 1bd6b92460..01a07a5f5e 100644 --- a/code/game/machinery/telecomms/presets_vr.dm +++ b/code/game/machinery/telecomms/presets_vr.dm @@ -3,3 +3,6 @@ hide = 1 produces_heat = 0 autolinkers = list("hb_relay") + +/obj/machinery/telecomms/relay/proc/reset_z() + listening_level = z diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index c1955399c2..d6cefb6828 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -32,7 +32,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() var/produces_heat = 1 //whether the machine will produce heat when on. var/delay = 10 // how many process() ticks to delay per heat var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub) - var/circuitboard = null // string pointing to a circuitboard type var/hide = 0 // Is it a hidden machine? var/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to. @@ -256,7 +255,17 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() idle_power_usage = 600 machinetype = 1 produces_heat = 0 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/receiver" + circuit = /obj/item/weapon/circuitboard/telecomms/receiver + +/obj/machinery/telecomms/receiver/Initialize() + . = ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/ansible(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + RefreshParts() /obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) @@ -312,7 +321,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() use_power = 1 idle_power_usage = 1600 machinetype = 7 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/hub" + circuit = /obj/item/weapon/circuitboard/telecomms/hub long_range_link = 1 netspeed = 40 var/list/telecomms_map @@ -320,6 +329,13 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() /obj/machinery/telecomms/hub/Initialize() . = ..() LAZYINITLIST(telecomms_map) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/stack/cable_coil(src, 2) + RefreshParts() /obj/machinery/telecomms/hub/process() . = ..() @@ -365,23 +381,25 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() idle_power_usage = 600 machinetype = 8 produces_heat = 0 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/relay" + circuit = /obj/item/weapon/circuitboard/telecomms/relay netspeed = 5 long_range_link = 1 var/broadcasting = 1 var/receiving = 1 -// VOREStation Edit - Make sure constructed relays keep relaying for their current Z when moved by shuttles. -/obj/machinery/telecomms/relay/proc/update_z() - if (initial(listening_level) == 0) - var/turf/T = get_turf(src) - listening_level = T.z - -/area/shuttle_arrived() +/obj/machinery/telecomms/relay/Initialize() . = ..() - for(var/obj/machinery/telecomms/relay/R in contents) - R.update_z() -// VOREStation Edit End + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/stack/cable_coil(src, 2) + RefreshParts() + +/obj/machinery/telecomms/relay/forceMove(var/newloc) + . = ..(newloc) + listening_level = z /obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) @@ -428,10 +446,19 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() use_power = 1 idle_power_usage = 1000 machinetype = 2 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/bus" + circuit = /obj/item/weapon/circuitboard/telecomms/bus netspeed = 40 var/change_frequency = 0 +/obj/machinery/telecomms/bus/Initialize() + . = ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + /obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) if(is_freq_listening(signal)) @@ -481,23 +508,37 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() idle_power_usage = 600 machinetype = 3 delay = 5 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/processor" + circuit = /obj/item/weapon/circuitboard/telecomms/processor var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals - receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) +/obj/machinery/telecomms/processor/Initialize() + . = ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/amplifier(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/analyzer(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/stack/cable_coil(src, 2) + RefreshParts() - if(is_freq_listening(signal)) +/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - if(process_mode) - signal.data["compression"] = 0 // uncompress subspace signal - else - signal.data["compression"] = 100 // even more compressed signal + if(is_freq_listening(signal)) - if(istype(machine_from, /obj/machinery/telecomms/bus)) - relay_direct_information(signal, machine_from) // send the signal back to the machine - else // no bus detected - send the signal to servers instead - signal.data["slow"] += rand(5, 10) // slow the signal down - relay_information(signal, "/obj/machinery/telecomms/server") + if(process_mode) + signal.data["compression"] = 0 // uncompress subspace signal + else + signal.data["compression"] = 100 // even more compressed signal + + if(istype(machine_from, /obj/machinery/telecomms/bus)) + relay_direct_information(signal, machine_from) // send the signal back to the machine + else // no bus detected - send the signal to servers instead + signal.data["slow"] += rand(5, 10) // slow the signal down + relay_information(signal, "/obj/machinery/telecomms/server") /* @@ -518,7 +559,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() use_power = 1 idle_power_usage = 300 machinetype = 4 - circuitboard = "/obj/item/weapon/circuitboard/telecomms/server" + circuit = /obj/item/weapon/circuitboard/telecomms/server var/list/log_entries = list() var/list/stored_names = list() var/list/TrafficActions = list() @@ -542,6 +583,15 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() Compiler.Holder = src server_radio = new() +/obj/machinery/telecomms/server/Initialize() + . = ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/subspace/sub_filter(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + /obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) if(signal.data["message"]) diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index d397c03cb1..663230b11f 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -706,6 +706,7 @@ /obj/item/weapon/reagent_containers/food/drinks/bottle/wine = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/small/ale = 15, /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer = 15, + /obj/item/weapon/reagent_containers/food/drinks/bottle/small/cider = 15, /obj/item/weapon/reagent_containers/food/drinks/bottle/orangejuice = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/tomatojuice = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/limejuice = 5, @@ -797,17 +798,17 @@ /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar = 8, /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 8, /obj/item/weapon/reagent_containers/pill/diet = 8, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5, + ///obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5, //VOREStation Removal, /obj/item/weapon/towel/random = 8) prices = list(/obj/item/weapon/reagent_containers/food/drinks/smallmilk = 3, /obj/item/weapon/reagent_containers/food/drinks/smallchocmilk = 3, - /obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/proteinshake = 40, //VOREStation Edit, + /obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/proteinshake = 40, //VOREStation Edit, /obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask = 5, /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar = 5, /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 5, /obj/item/weapon/reagent_containers/pill/diet = 25, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5, + ///obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5, //VOREStation Removal, /obj/item/weapon/towel/random = 40) contraband = list(/obj/item/weapon/reagent_containers/syringe/steroid = 4) @@ -1150,3 +1151,21 @@ /obj/item/toy/plushie/carp = 50, /obj/item/toy/plushie/deer = 50, /obj/item/toy/plushie/tabby_cat = 50) + +/obj/machinery/vending/fishing + name = "Loot Trawler" + desc = "A special vendor for fishing equipment." + product_ads = "Tired of trawling across the ocean floor? Get our loot!;Chum and rods.;Don't get baited into fishing without us!;Baby is your star-sign pisces? We'd make a perfect match.;Do not fear, plenty to catch around here.;Don't get reeled in helplessly, get your own rod today!" + icon_state = "fishvendor" + products = list(/obj/item/weapon/material/fishing_rod/modern/cheap = 6, + /obj/item/weapon/storage/box/wormcan = 4, + /obj/item/weapon/storage/box/wormcan/sickly = 10, + /obj/item/weapon/material/fishing_net = 2, + /obj/item/stack/cable_coil/random = 6) + prices = list(/obj/item/weapon/material/fishing_rod/modern/cheap = 50, + /obj/item/weapon/storage/box/wormcan = 12, + /obj/item/weapon/storage/box/wormcan/sickly = 6, + /obj/item/weapon/material/fishing_net = 40, + /obj/item/stack/cable_coil/random = 4) + premium = list(/obj/item/weapon/storage/box/wormcan/deluxe = 1) + contraband = list(/obj/item/weapon/storage/box/wormcan/deluxe = 1) diff --git a/code/game/machinery/vending_vr.dm b/code/game/machinery/vending_vr.dm index 6027bc5eaf..0bf7b94714 100644 --- a/code/game/machinery/vending_vr.dm +++ b/code/game/machinery/vending_vr.dm @@ -6,7 +6,6 @@ /obj/machinery/vending/security/New() products += list(/obj/item/weapon/gun/energy/taser = 8,/obj/item/weapon/gun/energy/stunrevolver = 4, /obj/item/weapon/reagent_containers/spray/pepper = 6,/obj/item/taperoll/police = 6, - /obj/item/weapon/gun/projectile/sec/flash = 4, /obj/item/ammo_magazine/m45/flash = 8, /obj/item/clothing/glasses/omnihud/sec = 6) ..() @@ -132,3 +131,18 @@ products += list(/obj/item/weapon/reagent_containers/food/snacks/liquidprotein = 8) prices += list(/obj/item/weapon/reagent_containers/food/snacks/liquidprotein = 10) ..() + +/obj/machinery/vending/blood + name = "Blood-Onator" + desc = "Freezer-vendor for storage and quick dispensing of blood packs" + product_ads = "The true life juice!;Vampire's choice!;Home-grown blood only!;Donate today, be saved tomorrow!;Approved by Zeng-Hu Pharmaceuticals Incorporated!; Curse you, Vey-Med artificial blood!" + icon_state = "blood" + idle_power_usage = 211 + req_access = list(access_medical) + products = list(/obj/item/weapon/reagent_containers/blood/prelabeled/APlus = 3,/obj/item/weapon/reagent_containers/blood/prelabeled/AMinus = 3, + /obj/item/weapon/reagent_containers/blood/prelabeled/BPlus = 3,/obj/item/weapon/reagent_containers/blood/prelabeled/BMinus = 3, + /obj/item/weapon/reagent_containers/blood/prelabeled/OPlus = 2,/obj/item/weapon/reagent_containers/blood/prelabeled/OMinus = 5, + /obj/item/weapon/reagent_containers/blood/empty = 5) + contraband = list(/obj/item/weapon/reagent_containers/glass/bottle/stoxin = 2) + req_log_access = access_cmo + has_logs = 1 \ No newline at end of file diff --git a/code/game/mecha/combat/combat.dm b/code/game/mecha/combat/combat.dm index 54e2b9c87b..b58956579a 100644 --- a/code/game/mecha/combat/combat.dm +++ b/code/game/mecha/combat/combat.dm @@ -2,7 +2,7 @@ force = 30 var/melee_cooldown = 10 var/melee_can_hit = 1 - var/list/destroyable_obj = list(/obj/mecha, /obj/structure/window, /obj/structure/grille, /turf/simulated/wall, /obj/structure/girder) + //var/list/destroyable_obj = list(/obj/mecha, /obj/structure/window, /obj/structure/grille, /turf/simulated/wall, /obj/structure/girder) internal_damage_threshold = 50 maint_access = 0 //add_req_access = 0 @@ -26,14 +26,15 @@ return */ -/obj/mecha/combat/melee_action(target as obj|mob|turf) +/obj/mecha/combat/melee_action(atom/T) if(internal_damage&MECHA_INT_CONTROL_LOST) - target = safepick(oview(1,src)) - if(!melee_can_hit || !istype(target, /atom)) return - if(istype(target, /mob/living)) - var/mob/living/M = target + T = safepick(oview(1,src)) + if(!melee_can_hit) + return + if(istype(T, /mob/living)) + var/mob/living/M = T if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) //Brains cannot change intents; Exo-piloting brains lack any form of physical feedback for control, limiting the ability to 'play nice'. - playsound(src, 'sound/weapons/punch4.ogg', 50, 1) + playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) if(damtype == "brute") step_away(M,src,15) /* @@ -44,8 +45,8 @@ melee_can_hit = 1 return */ - if(istype(target, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = target + if(ishuman(T)) + var/mob/living/carbon/human/H = T // if (M.health <= 0) return var/obj/item/organ/external/temp = H.get_organ(pick(BP_TORSO, BP_TORSO, BP_TORSO, BP_HEAD)) @@ -63,6 +64,8 @@ H.reagents.add_reagent("carpotoxin", force) if(H.reagents.get_reagent_amount("cryptobiolin") + force < force*2) H.reagents.add_reagent("cryptobiolin", force) + if("halloss") + H.stun_effect_act(1, force / 2, BP_TORSO, src) else return if(update) H.UpdateDamageIcon() @@ -84,12 +87,12 @@ else return M.updatehealth() - src.occupant_message("You hit [target].") - src.visible_message("[src.name] hits [target].") + src.occupant_message("You hit [T].") + src.visible_message("[src.name] hits [T].") else step_away(M,src) - src.occupant_message("You push [target] out of the way.") - src.visible_message("[src] pushes [target] out of the way.") + src.occupant_message("You push [T] out of the way.") + src.visible_message("[src] pushes [T] out of the way.") melee_can_hit = 0 if(do_after(melee_cooldown)) @@ -97,27 +100,23 @@ return else - if(damtype == "brute") - for(var/target_type in src.destroyable_obj) - if(istype(target, target_type) && hascall(target, "attackby")) - src.occupant_message("You hit [target].") - src.visible_message("[src.name] hits [target]") - if(!istype(target, /turf/simulated/wall) && !istype(target, /obj/structure/girder)) - target:attackby(src,src.occupant) - else if(prob(5)) - target:dismantle_wall(1) - src.occupant_message("You smash through the wall.") - src.visible_message("[src.name] smashes through the wall") - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - else if(istype(target, /turf/simulated/wall)) - target:take_damage(force) - else if(istype(target, /obj/structure/girder)) - target:take_damage(force * 3) //Girders have 200 health by default. Steel, non-reinforced walls take four punches, girders take (with this value-mod) two, girders took five without. - melee_can_hit = 0 + if(istype(T, /obj/machinery/disposal)) // Stops mechs from climbing into disposals + return + if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) // Don't smash unless we mean it + if(damtype == "brute") + src.occupant_message("You hit [T].") + src.visible_message("[src.name] hits [T]") + playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) - if(do_after(melee_cooldown)) - melee_can_hit = 1 - break + if(istype(T, /obj/structure/girder)) + T:take_damage(force * 3) //Girders have 200 health by default. Steel, non-reinforced walls take four punches, girders take (with this value-mod) two, girders took five without. + else + T:take_damage(force) + + melee_can_hit = 0 + + if(do_after(melee_cooldown)) + melee_can_hit = 1 return /* diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 978bbdeda8..19564acba9 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -120,4 +120,53 @@ ..() if (href_list["toggle_leg_overload"]) src.overload() + return + +/obj/mecha/combat/gygax/serenity + desc = "A lightweight exosuit made from a modified Gygax chassis combined with proprietary VeyMed medical tech. It's faster and sturdier than most medical mechs, but much of the armor plating has been stripped out, leaving it more vulnerable than a regular Gygax." + name = "Serenity" + icon_state = "medgax" + initial_icon = "medgax" + health = 150 + maxhealth = 150 + deflect_chance = 20 + step_in = 2 + damage_absorption = list("brute"=0.9,"fire"=1,"bullet"=0.9,"laser"=0.8,"energy"=0.9,"bomb"=1) + max_temperature = 20000 + overload_coeff = 1 + wreckage = /obj/effect/decal/mecha_wreckage/gygax/serenity + max_equip = 3 + step_energy_drain = 8 + cargo_capacity = 2 + max_hull_equip = 1 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + var/obj/item/clothing/glasses/hud/health/mech/hud + +/obj/mecha/combat/gygax/serenity/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health/mech(src) + return + +/obj/mecha/combat/gygax/serenity/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.glasses) + occupant_message("[H.glasses] prevent you from using [src] [hud]") + else + H.glasses = hud + H.recalculate_vis() + return 1 + else + return 0 + +/obj/mecha/combat/gygax/serenity/go_out() + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + if(H.glasses == hud) + H.glasses = null + H.recalculate_vis() + ..() return \ No newline at end of file diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index b54368a75e..3ada4824f7 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -133,7 +133,7 @@ ..() if(phasing) phasing = FALSE - radiation_repository.radiate(get_turf(src), 30) + SSradiation.radiate(get_turf(src), 30) log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1) visible_message("The [src.name] appears to flicker, before its silhouette stabilizes!") return diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 58b187670f..48a46fae56 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -15,6 +15,7 @@ icon_state = "mecha_equip" force = 5 origin_tech = list(TECH_MATERIAL = 2) + description_info = "Some equipment may gain new abilities or advantages if equipped to certain types of Exosuits." var/equip_cooldown = 0 var/equip_ready = 1 var/energy_drain = 0 @@ -25,6 +26,7 @@ var/equip_type = null //mechaequip2 var/allow_duplicate = FALSE var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed. + var/enable_special = FALSE // Will the tool do its special? /obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1) sleep(equip_cooldown) @@ -122,6 +124,15 @@ /obj/item/mecha_parts/mecha_equipment/proc/is_melee() return range&MELEE +/obj/item/mecha_parts/mecha_equipment/proc/enable_special_checks(atom/target) + if(ispath(required_type)) + return istype(target, required_type) + + for (var/path in required_type) + if (istype(target, path)) + return 1 + + return 0 /obj/item/mecha_parts/mecha_equipment/proc/action_checks(atom/target) if(!target) @@ -202,6 +213,10 @@ M.equipment += src chassis = M src.loc = M + + if(enable_special_checks(M)) + enable_special = TRUE + M.log_message("[src] initialized.") if(!M.selected) M.selected = src @@ -235,15 +250,14 @@ chassis.log_message("[src] removed from equipment.") chassis = null set_ready_state(1) + enable_special = FALSE return - /obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) if(href_list["detach"]) src.detach() return - /obj/item/mecha_parts/mecha_equipment/proc/set_ready_state(state) equip_ready = state if(chassis) @@ -259,3 +273,6 @@ if(chassis) chassis.log_message("[src]: [message]") return + +/obj/item/mecha_parts/mecha_equipment/proc/MoveAction() //Allows mech equipment to do an action upon the mech moving + return diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index f763304fc0..9f1e522733 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -6,11 +6,11 @@ origin_tech = list(TECH_DATA = 2, TECH_BIO = 3) energy_drain = 20 range = MELEE - equip_cooldown = 50 + equip_cooldown = 30 var/mob/living/carbon/human/occupant = null var/datum/global_iterator/pr_mech_sleeper var/inject_amount = 5 - required_type = /obj/mecha/medical + required_type = list(/obj/mecha/medical) salvageable = 0 allow_duplicate = TRUE @@ -247,144 +247,6 @@ return -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer - name = "Cable Layer" - icon_state = "mecha_wire" - var/datum/event/event - var/turf/old_turf - var/obj/structure/cable/last_piece - var/obj/item/stack/cable_coil/cable - var/max_cable = 1000 - required_type = /obj/mecha/working - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/New() - cable = new(src) - cable.amount = 0 - ..() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/attach() - ..() - event = chassis.events.addEvent("onMove",src,"layCable") - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/detach() - chassis.events.clearEvent("onMove",event) - return ..() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/destroy() - chassis.events.clearEvent("onMove",event) - return ..() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/action(var/obj/item/stack/cable_coil/target) - if(!action_checks(target)) - return - var/result = load_cable(target) - var/message - if(isnull(result)) - message = "Unable to load [target] - no cable found." - else if(!result) - message = "Reel is full." - else - message = "[result] meters of cable successfully loaded." - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - occupant_message(message) - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/Topic(href,href_list) - ..() - if(href_list["toggle"]) - set_ready_state(!equip_ready) - occupant_message("[src] [equip_ready?"dea":"a"]ctivated.") - log_message("[equip_ready?"Dea":"A"]ctivated.") - return - if(href_list["cut"]) - if(cable && cable.amount) - var/m = round(input(chassis.occupant,"Please specify the length of cable to cut","Cut cable",min(cable.amount,30)) as num, 1) - m = min(m, cable.amount) - if(m) - use_cable(m) - var/obj/item/stack/cable_coil/CC = new (get_turf(chassis)) - CC.amount = m - else - occupant_message("There's no more cable on the reel.") - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/get_equip_info() - var/output = ..() - if(output) - return "[output] \[Cable: [cable ? cable.amount : 0] m\][(cable && cable.amount) ? "- [!equip_ready?"Dea":"A"]ctivate|Cut" : null]" - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/load_cable(var/obj/item/stack/cable_coil/CC) - if(istype(CC) && CC.amount) - var/cur_amount = cable? cable.amount : 0 - var/to_load = max(max_cable - cur_amount,0) - if(to_load) - to_load = min(CC.amount, to_load) - if(!cable) - cable = new(src) - cable.amount = 0 - cable.amount += to_load - CC.use(to_load) - return to_load - else - return 0 - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/use_cable(amount) - if(!cable || cable.amount<1) - set_ready_state(1) - occupant_message("Cable depleted, [src] deactivated.") - log_message("Cable depleted, [src] deactivated.") - return - if(cable.amount < amount) - occupant_message("No enough cable to finish the task.") - return - cable.use(amount) - update_equip_info() - return 1 - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/reset() - last_piece = null - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/dismantleFloor(var/turf/new_turf) - if(istype(new_turf, /turf/simulated/floor)) - var/turf/simulated/floor/T = new_turf - if(!T.is_plating()) - T.make_plating(!(T.broken || T.burnt)) - return new_turf.is_plating() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/layCable(var/turf/new_turf) - if(equip_ready || !istype(new_turf) || !dismantleFloor(new_turf)) - return reset() - var/fdirn = turn(chassis.dir,180) - for(var/obj/structure/cable/LC in new_turf) // check to make sure there's not a cable there already - if(LC.d1 == fdirn || LC.d2 == fdirn) - return reset() - if(!use_cable(1)) - return reset() - var/obj/structure/cable/NC = new(new_turf) - NC.cableColor("red") - NC.d1 = 0 - NC.d2 = fdirn - NC.update_icon() - - var/datum/powernet/PN - if(last_piece && last_piece.d2 != chassis.dir) - last_piece.d1 = min(last_piece.d2, chassis.dir) - last_piece.d2 = max(last_piece.d2, chassis.dir) - last_piece.update_icon() - PN = last_piece.powernet - - if(!PN) - PN = new() - PN.add_cable(NC) - NC.mergeConnectedNetworks(NC.d2) - - //NC.mergeConnectedNetworksOnTurf() - last_piece = NC - return 1 - /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun name = "syringe gun" desc = "Exosuit-mounted chem synthesizer with syringe gun. Reagents inside are held in stasis, so no reactions will occur. (Can be attached to: Medical Exosuits)" @@ -402,7 +264,7 @@ range = MELEE|RANGED equip_cooldown = 10 origin_tech = list(TECH_MATERIAL = 3, TECH_BIO = 4, TECH_MAGNET = 4, TECH_DATA = 3) - required_type = /obj/mecha/medical + required_type = list(/obj/mecha/medical) //This is a list of datums so as to allow id changes, and force compile errors if removed. var/static/list/allowed_reagents = list( @@ -661,6 +523,7 @@ occupant_message("No reagent info gained from [A].") return 0 occupant_message("Analyzing reagents...") + //VOREStation Block Edit - Start for(var/datum/reagent/R in A.reagents.reagent_list) if(R.id in known_reagents) occupant_message("Reagent \"[R.name]\" already present in database, skipping.") @@ -669,7 +532,8 @@ send_byjax(chassis.occupant,"msyringegun.browser","reagents_form",get_reagents_form()) else occupant_message("Reagent \"[R.name]\" unable to be scanned, skipping.") - occupant_message("Analyzis complete.") + //VOREstation Block Edit - End + occupant_message("Analysis complete.") return 1 /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/add_known_reagent(r_id,r_name) @@ -710,3 +574,232 @@ S.reagents.add_reagent(reagent,amount) S.chassis.use_power(energy_drain) return 1 + +/obj/item/mecha_parts/mecha_equipment/crisis_drone + name = "crisis dronebay" + desc = "A small shoulder-mounted dronebay containing a rapid response drone capable of moderately stabilizing a patient near the exosuit." + icon_state = "mecha_dronebay" + origin_tech = list(TECH_PHORON = 3, TECH_MAGNET = 6, TECH_BIO = 5, TECH_DATA = 4) + range = MELEE|RANGED + equip_cooldown = 3 SECONDS + required_type = list(/obj/mecha/medical) + + var/droid_state = "med_droid" + + var/beam_state = "medbeam" + + var/enabled = FALSE + + var/icon/drone_overlay + + var/max_distance = 3 + + var/damcap = 60 + var/heal_dead = FALSE // Does this device heal the dead? + + var/brute_heal = 0.5 // Amount of bruteloss healed. + var/burn_heal = 0.5 // Amount of fireloss healed. + var/tox_heal = 0.5 // Amount of toxloss healed. + var/oxy_heal = 1 // Amount of oxyloss healed. + var/rad_heal = 0 // Amount of radiation healed. + var/clone_heal = 0 // Amount of cloneloss healed. + var/hal_heal = 0.2 // Amount of halloss healed. + var/bone_heal = 0 // Percent chance it will heal a broken bone. this does not mean 'make it not instantly re-break'. + + var/mob/living/Target = null + var/datum/beam/MyBeam = null + + equip_type = EQUIP_HULL + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/Initialize() + ..() + drone_overlay = new(src.icon, icon_state = droid_state) + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/Destroy() + STOP_PROCESSING(SSobj, src) + ..() + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/attach(obj/mecha/M as obj) + . = ..(M) + if(chassis) + START_PROCESSING(SSobj, src) + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/detach(atom/moveto=null) + shut_down() + . = ..(moveto) + STOP_PROCESSING(SSobj, src) + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/critfail() + . = ..() + STOP_PROCESSING(SSobj, src) + shut_down() + if(chassis && chassis.occupant) + to_chat(chassis.occupant, "\The [chassis] shudders as something jams!") + log_message("[src.name] has malfunctioned. Maintenance required.") + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/process() // Will continually try to find the nearest person above the threshold that is a valid target, and try to heal them. + if(chassis && enabled && chassis.has_charge(energy_drain) && (chassis.occupant || enable_special)) + var/mob/living/Targ = Target + var/TargDamage = 0 + + if(!valid_target(Target)) + Target = null + + if(Target) + TargDamage = (Targ.getOxyLoss() + Targ.getFireLoss() + Targ.getBruteLoss() + Targ.getToxLoss()) + + for(var/mob/living/Potential in viewers(max_distance, chassis)) + if(!valid_target(Potential)) + continue + + var/tallydamage = 0 + if(oxy_heal) + tallydamage += Potential.getOxyLoss() + if(burn_heal) + tallydamage += Potential.getFireLoss() + if(brute_heal) + tallydamage += Potential.getBruteLoss() + if(tox_heal) + tallydamage += Potential.getToxLoss() + if(hal_heal) + tallydamage += Potential.getHalLoss() + if(clone_heal) + tallydamage += Potential.getCloneLoss() + if(rad_heal) + tallydamage += Potential.radiation / 2 + + if(tallydamage > TargDamage) + Target = Potential + + if(MyBeam && !valid_target(MyBeam.target)) + QDEL_NULL(MyBeam) + + if(Target) + if(MyBeam && MyBeam.target != Target) + QDEL_NULL(MyBeam) + + if(valid_target(Target)) + if(!MyBeam) + MyBeam = chassis.Beam(Target,icon='icons/effects/beam.dmi',icon_state=beam_state,time=3 SECONDS,maxdistance=max_distance,beam_type = /obj/effect/ebeam,beam_sleep_time=2) + heal_target(Target) + + else + shut_down() + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/proc/valid_target(var/mob/living/L) + . = TRUE + + if(!L || !istype(L)) + return FALSE + + if(get_dist(L, src) > max_distance) + return FALSE + + if(!(L in viewers(max_distance, chassis))) + return FALSE + + if(!unique_patient_checks(L)) + return FALSE + + if(L.stat == DEAD && !heal_dead) + return FALSE + + var/tallydamage = 0 + if(oxy_heal) + tallydamage += L.getOxyLoss() + if(burn_heal) + tallydamage += L.getFireLoss() + if(brute_heal) + tallydamage += L.getBruteLoss() + if(tox_heal) + tallydamage += L.getToxLoss() + if(hal_heal) + tallydamage += L.getHalLoss() + if(clone_heal) + tallydamage += L.getCloneLoss() + if(rad_heal) + tallydamage += L.radiation / 2 + + if(tallydamage < damcap) + return FALSE + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/proc/shut_down() + if(enabled) + chassis.visible_message("\The [chassis]'s [src] buzzes as its drone returns to port.") + toggle_drone() + if(!isnull(Target)) + Target = null + if(MyBeam) + QDEL_NULL(MyBeam) + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/proc/unique_patient_checks(var/mob/living/L) // Anything special for subtypes. Does it only work on Robots? Fleshies? A species? + . = TRUE + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/proc/heal_target(var/mob/living/L) // We've done all our special checks, just get to fixing damage. + chassis.use_power(energy_drain) + if(istype(L)) + L.adjustBruteLoss(brute_heal * -1) + L.adjustFireLoss(burn_heal * -1) + L.adjustToxLoss(tox_heal * -1) + L.adjustOxyLoss(oxy_heal * -1) + L.adjustCloneLoss(clone_heal * -1) + L.adjustHalLoss(hal_heal * -1) + L.radiation = max(0, L.radiation - rad_heal) + + if(ishuman(L) && bone_heal) + var/mob/living/carbon/human/H = L + + if(H.bad_external_organs.len) + for(var/obj/item/organ/external/E in H.bad_external_organs) + if(prob(bone_heal)) + E.status &= ~ORGAN_BROKEN + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/proc/toggle_drone() + ..() + if(chassis) + enabled = !enabled + if(enabled) + set_ready_state(0) + log_message("Activated.") + chassis.overlays += drone_overlay + else + set_ready_state(1) + log_message("Deactivated.") + chassis.overlays -= drone_overlay + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/Topic(href, href_list) + ..() + if(href_list["toggle_drone"]) + toggle_drone() + return + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/get_equip_info() + if(!chassis) return + return "* [src.name] - [enabled?"Dea":"A"]ctivate" + +/obj/item/mecha_parts/mecha_equipment/crisis_drone/rad + name = "hazmat dronebay" + desc = "A small shoulder-mounted dronebay containing a rapid response drone capable of purging a patient near the exosuit of radiation damage." + icon_state = "mecha_dronebay_rad" + + droid_state = "rad_drone" + beam_state = "g_beam" + + tox_heal = 0.5 + rad_heal = 5 + clone_heal = 0.2 + hal_heal = 0.2 + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/medanalyzer + name = "mounted humanoid scanner" + desc = "An exosuit-mounted scanning device." + icon_state = "mecha_analyzer_health" + origin_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 5, TECH_BIO = 5) + equip_cooldown = 5 SECONDS + energy_drain = 100 + range = MELEE + equip_type = EQUIP_UTILITY + ready_sound = 'sound/weapons/flash.ogg' + required_type = list(/obj/mecha/medical) + + tooltype = /obj/item/device/healthanalyzer/advanced diff --git a/code/game/mecha/equipment/tools/medical_tools_vr.dm b/code/game/mecha/equipment/tools/medical_tools_vr.dm new file mode 100644 index 0000000000..19efa0a5fd --- /dev/null +++ b/code/game/mecha/equipment/tools/medical_tools_vr.dm @@ -0,0 +1,10 @@ +/obj/item/mecha_parts/mecha_equipment/weapon/energy/medigun + equip_cooldown = 6 + name = "\improper BL-3 \"Phoenix\" directed restoration system" + desc = "The BL-3 'Phoenix' is a portable medical system used to treat external injuries from afar." + icon_state = "mecha_medbeam" + energy_drain = 1000 + projectile = /obj/item/projectile/beam/medigun + fire_sound = 'sound/weapons/eluger.ogg' + equip_type = EQUIP_UTILITY + origin_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_BIO = 6, TECH_POWER = 6) \ No newline at end of file diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index b621d97cba..73f7356ade 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -8,11 +8,13 @@ energy_drain = 10 var/dam_force = 20 var/obj/mecha/working/ripley/cargo_holder - required_type = /obj/mecha/working + required_type = list(/obj/mecha/working) + ready_sound = 'sound/mecha/gasdisconnected.ogg' /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/attach(obj/mecha/M as obj) ..() cargo_holder = M + return /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/action(atom/target) @@ -28,7 +30,48 @@ occupant_message("You can't load living things into the cargo compartment.") return if(O.anchored) - occupant_message("[target] is firmly secured.") + if(enable_special) + if(istype(O, /obj/machinery/door/firedoor)) // I love doors. + var/obj/machinery/door/firedoor/FD = O + if(FD.blocked) + FD.visible_message("\The [chassis] begins prying on \the [FD]!") + if(do_after(chassis.occupant,10 SECONDS,FD)) + playsound(FD.loc, 'sound/machines/airlock_creaking.ogg', 100, 1) + FD.blocked = 0 + FD.update_icon() + FD.open(1) + FD.visible_message("\The [chassis] tears \the [FD] open!") + else if(FD.density) + FD.visible_message("\The [chassis] begins forcing \the [FD] open!") + if(do_after(chassis.occupant, 5 SECONDS,FD)) + playsound(FD.loc, 'sound/machines/airlock_creaking.ogg', 100, 1) + FD.visible_message("\The [chassis] forces \the [FD] open!") + FD.open(1) + else + FD.visible_message("\The [chassis] forces \the [FD] closed!") + FD.close(1) + else if(istype(O, /obj/machinery/door/airlock)) // D o o r s. + var/obj/machinery/door/airlock/AD = O + if(AD.locked) + occupant_message("The airlock's bolts prevent it from being forced.") + else if(!AD.operating) + if(AD.welded) + AD.visible_message("\The [chassis] begins prying on \the [AD]!") + if(do_after(chassis.occupant, 15 SECONDS,AD) && chassis.Adjacent(AD)) + AD.welded = FALSE + AD.update_icon() + playsound(AD.loc, 'sound/machines/airlock_creaking.ogg', 100, 1) + AD.visible_message("\The [chassis] tears \the [AD] open!") + if(!AD.welded) + if(density) + spawn(0) + AD.open(1) + else + spawn(0) + AD.close(1) + return + else + occupant_message("[target] is firmly secured.") return if(cargo_holder.cargo.len >= cargo_holder.cargo_capacity) occupant_message("Not enough room in cargo compartment.") @@ -62,6 +105,15 @@ occupant_message("You squeeze [target] with [src.name]. Something cracks.") playsound(src.loc, "fracture", 5, 1, -2) //CRACK chassis.visible_message("[chassis] squeezes [target].") + else if(chassis.occupant.a_intent == I_DISARM && enable_special) + playsound(src.loc, 'sound/mecha/hydraulic.ogg', 10, 1, -2) + M.take_overall_damage(dam_force/2) + M.adjustOxyLoss(round(dam_force/3)) + M.updatehealth() + occupant_message("You slam [target] with [src.name]. Something cracks.") + playsound(src.loc, "fracture", 3, 1, -2) //CRACK 2 + chassis.visible_message("[chassis] slams [target].") + M.throw_at(get_step(M,get_dir(src, M)), 14, 1.5, chassis) else step_away(M,chassis) occupant_message("You push [target] out of the way.") @@ -73,12 +125,12 @@ /obj/item/mecha_parts/mecha_equipment/tool/drill name = "drill" - desc = "This is the drill that'll pierce the heavens! (Can be attached to: Combat and Engineering Exosuits)" + desc = "This is the drill that'll pierce the heavens!" icon_state = "mecha_drill" equip_cooldown = 30 energy_drain = 10 force = 15 - required_type = list(/obj/mecha/working/ripley, /obj/mecha/combat) + required_type = list(/obj/mecha/working/ripley) /obj/item/mecha_parts/mecha_equipment/tool/drill/action(atom/target) if(!action_checks(target)) return @@ -101,23 +153,20 @@ log_message("Drilled through [target]") target.ex_act(2) else if(istype(target, /turf/simulated/mineral)) - for(var/turf/simulated/mineral/M in range(chassis,1)) - if(get_dir(chassis,M)&chassis.dir) - M.GetDrilled() + if(enable_special) + for(var/turf/simulated/mineral/M in range(chassis,1)) + if(get_dir(chassis,M)&chassis.dir) + M.GetDrilled() + else + var/turf/simulated/mineral/M1 = target + M1.GetDrilled() log_message("Drilled through [target]") if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in chassis.equipment) var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo if(ore_box) for(var/obj/item/weapon/ore/ore in range(chassis,1)) if(get_dir(chassis,ore)&chassis.dir) - ore.Move(ore_box) - log_message("Drilled through [target]") - if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in chassis.equipment) - var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo - if(ore_box) - for(var/obj/item/weapon/ore/ore in range(chassis,1)) - if(get_dir(chassis,ore)&chassis.dir) - ore.Move(ore_box) + ore.forceMove(ore_box) else if(target.loc == C) log_message("Drilled through [target]") target.ex_act(2) @@ -125,7 +174,7 @@ /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill name = "diamond drill" - desc = "This is an upgraded version of the drill that'll pierce the heavens! (Can be attached to: Combat and Engineering Exosuits)" + desc = "This is an upgraded version of the drill that'll pierce the heavens!" icon_state = "mecha_diamond_drill" origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3) equip_cooldown = 10 @@ -150,21 +199,115 @@ log_message("Drilled through [target]") target.ex_act(3) else if(istype(target, /turf/simulated/mineral)) - for(var/turf/simulated/mineral/M in range(chassis,1)) - if(get_dir(chassis,M)&chassis.dir) - M.GetDrilled() + if(enable_special) + for(var/turf/simulated/mineral/M in range(chassis,1)) + if(get_dir(chassis,M)&chassis.dir) + M.GetDrilled() + else + var/turf/simulated/mineral/M1 = target + M1.GetDrilled() log_message("Drilled through [target]") if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in chassis.equipment) var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo if(ore_box) for(var/obj/item/weapon/ore/ore in range(chassis,1)) if(get_dir(chassis,ore)&chassis.dir) - ore.Move(ore_box) + ore.forceMove(ore_box) else if(target.loc == C) log_message("Drilled through [target]") target.ex_act(2) return 1 +/obj/item/mecha_parts/mecha_equipment/tool/drill/bore + name = "depth bore" + desc = "This is the drill that'll pierce the depths!" + icon_state = "mecha_bore" + equip_cooldown = 5 SECONDS + energy_drain = 30 + force = 20 + required_type = list(/obj/mecha/working/ripley) + +/obj/item/mecha_parts/mecha_equipment/tool/drill/bore/action(atom/target) + if(!action_checks(target)) return + if(isobj(target)) + var/obj/target_obj = target + if(target_obj.unacidable) return + set_ready_state(0) + chassis.use_power(energy_drain) + chassis.visible_message("[chassis] starts to bore into \the [target]", "You hear the bore.") + occupant_message("You start to bore into \the [target]") + var/T = chassis.loc + var/C = target.loc + if(do_after_cooldown(target)) + if(T == chassis.loc && src == chassis.selected) + if(istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target + if(W.reinf_material) + occupant_message("[target] is too durable to bore through.") + else + log_message("Bored through [target]") + target.ex_act(2) + else if(istype(target, /turf/simulated/mineral)) + var/turf/simulated/mineral/M = target + if(enable_special && !M.density) + M.ex_act(2) + log_message("Bored into [target]") + else + M.GetDrilled() + log_message("Bored through [target]") + if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in chassis.equipment) + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo + if(ore_box) + for(var/obj/item/weapon/ore/ore in range(chassis,1)) + if(get_dir(chassis,ore)&chassis.dir) + ore.forceMove(ore_box) + else if(target.loc == C) + log_message("Drilled through [target]") + target.ex_act(2) + return 1 + +/obj/item/mecha_parts/mecha_equipment/tool/orescanner + name = "mounted ore scanner" + desc = "An exosuit-mounted ore scanner." + icon_state = "mecha_analyzer" + origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 2, TECH_POWER = 2) + equip_cooldown = 5 + energy_drain = 30 + range = MELEE|RANGED + equip_type = EQUIP_SPECIAL + ready_sound = 'sound/items/goggles_charge.ogg' + required_type = list(/obj/mecha/working/ripley) + + var/obj/item/weapon/mining_scanner/my_scanner = null + var/exact_scan = FALSE + +/obj/item/mecha_parts/mecha_equipment/tool/orescanner/Initialize() + my_scanner = new(src) + return ..() + +/obj/item/mecha_parts/mecha_equipment/tool/orescanner/Destroy() + QDEL_NULL(my_scanner) + return ..() + +/obj/item/mecha_parts/mecha_equipment/tool/orescanner/action(var/atom/target) + if(!action_checks(target) || get_dist(chassis, target) > 5) + return FALSE + + if(!enable_special) + target = get_turf(chassis) + + var/datum/beam/ScanBeam = chassis.Beam(target,"g_beam",'icons/effects/beam.dmi',time=2 SECONDS,10,/obj/effect/ebeam,2) + + if(do_after(chassis.occupant, 2 SECONDS)) + my_scanner.ScanTurf(target, chassis.occupant, exact_scan) + + QDEL_NULL(ScanBeam) + +/obj/item/mecha_parts/mecha_equipment/tool/orescanner/advanced + name = "advanced ore scanner" + icon_state = "mecha_analyzer_adv" + exact_scan = TRUE + /obj/item/mecha_parts/mecha_equipment/tool/extinguisher name = "extinguisher" desc = "Exosuit-mounted extinguisher (Can be attached to: Engineering exosuits)" @@ -172,7 +315,7 @@ equip_cooldown = 5 energy_drain = 0 range = MELEE|RANGED - required_type = /obj/mecha/working + required_type = list(/obj/mecha/working) var/spray_particles = 5 var/spray_amount = 5 //units of liquid per particle. 5 is enough to wet the floor - it's a big fire extinguisher, so should be fine var/max_water = 1000 @@ -235,6 +378,48 @@ /obj/item/mecha_parts/mecha_equipment/tool/extinguisher/on_reagent_change() return +/obj/item/mecha_parts/mecha_equipment/tool/powertool + name = "pneumatic wrench" + desc = "An exosuit-mounted hydraulic wrench." + icon_state = "mecha_wrench" + origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 2, TECH_POWER = 2) + equip_cooldown = 3 + energy_drain = 15 + range = MELEE + equip_type = EQUIP_UTILITY + ready_sound = 'sound/items/Ratchet.ogg' + required_type = list(/obj/mecha/working/ripley) + + var/obj/item/my_tool = null + var/tooltype = /obj/item/weapon/tool/wrench/power + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/Initialize() + my_tool = new tooltype(src) + my_tool.name = name + my_tool.anchored = TRUE + my_tool.canremove = FALSE + return ..() + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/Destroy() + QDEL_NULL(my_tool) + return ..() + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/action(var/atom/target) + if(!action_checks(target)) + return FALSE + + if(isliving(target)) + my_tool.attack(target, chassis.occupant, BP_TORSO) + + target.attackby(my_tool,chassis.occupant) + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/prybar + name = "pneumatic prybar" + desc = "An exosuit-mounted pneumatic prybar." + icon_state = "mecha_crowbar" + tooltype = /obj/item/weapon/tool/crowbar/power + ready_sound = 'sound/mecha/gasdisconnected.ogg' + /obj/item/mecha_parts/mecha_equipment/tool/rcd name = "mounted RCD" desc = "An exosuit-mounted Rapid Construction Device. (Can be attached to: Any exosuit)" @@ -1016,7 +1201,7 @@ /datum/global_iterator/mecha_generator/nuclear/process(var/obj/item/mecha_parts/mecha_equipment/generator/nuclear/EG) if(..()) - radiation_repository.radiate(EG, (EG.rad_per_cycle * 3)) + SSradiation.radiate(EG, (EG.rad_per_cycle * 3)) return 1 @@ -1029,7 +1214,7 @@ energy_drain = 0 var/dam_force = 0 var/obj/mecha/working/ripley/cargo_holder - required_type = /obj/mecha/working/ripley + required_type = list(/obj/mecha/working/ripley) equip_type = EQUIP_SPECIAL @@ -1330,3 +1515,154 @@ sleep(equip_cooldown) wait = 0 return 1 + +/obj/item/mecha_parts/mecha_equipment/speedboost + name = "ripley leg actuator overdrive" + desc = "System enhancements and overdrives to make a ripley's legs move faster." + icon_state = "tesla" + origin_tech = list( TECH_POWER = 5, TECH_MATERIAL = 4, TECH_ENGINEERING = 4) + required_type = list(/obj/mecha/working/ripley) + + equip_type = EQUIP_HULL + +/obj/item/mecha_parts/mecha_equipment/speedboost/attach(obj/mecha/M as obj) + ..() + if(enable_special) + chassis.step_in = (chassis.step_in-2) // Make the ripley as fast as a durand + else + chassis.step_in = (chassis.step_in+1) // Improper parts slow the mech down + return + +/obj/item/mecha_parts/mecha_equipment/speedboost/detach() + chassis.step_in = initial(chassis.step_in) + ..() + return + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer + name = "Cable Layer" + icon_state = "mecha_wire" + var/turf/old_turf + var/obj/structure/cable/last_piece + var/obj/item/stack/cable_coil/cable + var/max_cable = 1000 + required_type = list(/obj/mecha/working) + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/New() + cable = new(src) + cable.amount = 0 + ..() + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/MoveAction() + layCable() + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/action(var/obj/item/stack/cable_coil/target) + if(!action_checks(target)) + return + var/result = load_cable(target) + var/message + if(isnull(result)) + message = "Unable to load [target] - no cable found." + else if(!result) + message = "Reel is full." + else + message = "[result] meters of cable successfully loaded." + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + occupant_message(message) + return + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/Topic(href,href_list) + ..() + if(href_list["toggle"]) + set_ready_state(!equip_ready) + occupant_message("[src] [equip_ready?"dea":"a"]ctivated.") + log_message("[equip_ready?"Dea":"A"]ctivated.") + return + if(href_list["cut"]) + if(cable && cable.amount) + var/m = round(input(chassis.occupant,"Please specify the length of cable to cut","Cut cable",min(cable.amount,30)) as num, 1) + m = min(m, cable.amount) + if(m) + use_cable(m) + var/obj/item/stack/cable_coil/CC = new (get_turf(chassis)) + CC.amount = m + else + occupant_message("There's no more cable on the reel.") + return + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/get_equip_info() + var/output = ..() + if(output) + return "[output] \[Cable: [cable ? cable.amount : 0] m\][(cable && cable.amount) ? "- [!equip_ready?"Dea":"A"]ctivate|Cut" : null]" + return + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/load_cable(var/obj/item/stack/cable_coil/CC) + if(istype(CC) && CC.amount) + var/cur_amount = cable? cable.amount : 0 + var/to_load = max(max_cable - cur_amount,0) + if(to_load) + to_load = min(CC.amount, to_load) + if(!cable) + cable = new(src) + cable.amount = 0 + cable.amount += to_load + CC.use(to_load) + return to_load + else + return 0 + return + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/use_cable(amount) + if(!cable || cable.amount<1) + set_ready_state(1) + occupant_message("Cable depleted, [src] deactivated.") + log_message("Cable depleted, [src] deactivated.") + return + if(cable.amount < amount) + occupant_message("No enough cable to finish the task.") + return + cable.use(amount) + update_equip_info() + return 1 + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/reset() + last_piece = null + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/dismantleFloor(var/turf/new_turf) + new_turf = get_turf(chassis) + if(istype(new_turf, /turf/simulated/floor)) + var/turf/simulated/floor/T = new_turf + if(!T.is_plating()) + T.make_plating(!(T.broken || T.burnt)) + return new_turf.is_plating() + +/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/layCable(var/turf/new_turf) + new_turf = get_turf(chassis) + if(equip_ready || !istype(new_turf, /turf/simulated/floor) || !dismantleFloor(new_turf)) + return reset() + var/fdirn = turn(chassis.dir,180) + for(var/obj/structure/cable/LC in new_turf) // check to make sure there's not a cable there already + if(LC.d1 == fdirn || LC.d2 == fdirn) + return reset() + if(!use_cable(1)) + return reset() + var/obj/structure/cable/NC = new(new_turf) + NC.cableColor("red") + NC.d1 = 0 + NC.d2 = fdirn + NC.update_icon() + + var/datum/powernet/PN + if(last_piece && last_piece.d2 != chassis.dir) + last_piece.d1 = min(last_piece.d2, chassis.dir) + last_piece.d2 = max(last_piece.d2, chassis.dir) + last_piece.update_icon() + PN = last_piece.powernet + + if(!PN) + PN = new() + PN.add_cable(NC) + NC.mergeConnectedNetworks(NC.d2) + + //NC.mergeConnectedNetworksOnTurf() + last_piece = NC + return 1 \ No newline at end of file diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 2be7566822..5df2c54d10 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -34,11 +34,18 @@ var/turf/aimloc = targloc if(deviation) aimloc = locate(targloc.x+GaussRandRound(deviation,1),targloc.y+GaussRandRound(deviation,1),targloc.z) - if(!aimloc || aimloc == curloc) + if(!aimloc || aimloc == curloc || (locs && aimloc in locs)) break playsound(chassis, fire_sound, fire_volume, 1) projectiles-- - var/P = new projectile(curloc) + var/turf/projectile_turf + if(chassis.locs && chassis.locs.len) // Multi tile. + for(var/turf/Tloc in chassis.locs) + if(get_dist(Tloc, aimloc) < get_dist(loc, aimloc)) + projectile_turf = get_turf(Tloc) + if(!projectile_turf) + projectile_turf = get_turf(curloc) + var/P = new projectile(projectile_turf) Fire(P, target, params) if(i == 1) set_ready_state(0) @@ -61,11 +68,14 @@ return /obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, params) - var/obj/item/projectile/P = A - P.dispersion = deviation - process_accuracy(P, chassis.occupant, target) - P.preparePixelProjectile(target, chassis.occupant, params) - P.fire() + if(istype(A, /obj/item/projectile)) // Sanity. + var/obj/item/projectile/P = A + P.dispersion = deviation + process_accuracy(P, chassis.occupant, target) + P.launch_projectile_from_turf(target, chassis.occupant.zone_sel.selecting, chassis.occupant, params) + else if(istype(A, /atom/movable)) + var/atom/movable/AM = A + AM.throw_at(target, 7, 1, chassis) /obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target) var/obj/item/projectile/P = projectile @@ -411,10 +421,12 @@ icon_state = "missile" var/primed = null throwforce = 15 + catchable = 0 var/devastation = 0 var/heavy_blast = 1 var/light_blast = 2 var/flash_blast = 4 + does_spin = FALSE // No fun corkscrew missiles. /obj/item/missile/proc/warhead_special(var/target) explosion(target, devastation, heavy_blast, light_blast, flash_blast) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 9b17cb8613..74cdb2d368 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -49,6 +49,8 @@ var/mech_faction = null var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once. + var/stomp_sound = 'sound/mecha/mechstep.ogg' + var/swivel_sound = 'sound/mecha/mechturn.ogg' //inner atmos var/use_internal_tank = 0 @@ -382,9 +384,16 @@ /obj/mecha/Move() . = ..() if(.) - events.fireEvent("onMove",get_turf(src)) + MoveAction() return +/obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves + if(!equipment.len) + return + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + ME.MoveAction() + /obj/mecha/relaymove(mob/user,direction) if(user != src.occupant) //While not "realistic", this piece is player friendly. if(istype(user,/mob/living/carbon/brain)) @@ -441,21 +450,24 @@ /obj/mecha/proc/mechturn(direction) set_dir(direction) - playsound(src,'sound/mecha/mechturn.ogg',40,1) + if(swivel_sound) + playsound(src,swivel_sound,40,1) return 1 /obj/mecha/proc/mechstep(direction) - var/result = step(src,direction) - if(result) - playsound(src,"mechstep",40,1) + var/result = get_step(src,direction) + if(result && Move(result)) + if(stomp_sound) + playsound(src,stomp_sound,40,1) handle_equipment_movement() return result /obj/mecha/proc/mechsteprand() - var/result = step_rand(src) - if(result) - playsound(src,"mechstep",40,1) + var/result = get_step_rand(src) + if(result && Move(result)) + if(stomp_sound) + playsound(src,stomp_sound,40,1) handle_equipment_movement() return result @@ -473,9 +485,10 @@ else //I have no idea why I disabled this obstacle.Bumped(src) else if(istype(obstacle, /mob)) - step(obstacle,src.dir) + var/mob/M = obstacle + M.Move(get_step(obstacle,src.dir)) else - obstacle.Bumped(src) + . = ..(obstacle) return /////////////////////////////////// @@ -529,7 +542,7 @@ //////// Health related procs //////// //////////////////////////////////////// -/obj/mecha/proc/take_damage(amount, type="brute") +/obj/mecha/take_damage(amount, type="brute") if(amount) var/damage = absorbDamage(amount,type) health -= damage @@ -615,6 +628,11 @@ /obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper + if(istype(Proj, /obj/item/projectile/test)) + var/obj/item/projectile/test/Test = Proj + Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. + return + src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1) call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment ..() diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 9edff107b1..3cbe670a41 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -560,8 +560,294 @@ feedback_inc("mecha_gygax_created",1) return + + ////////////////////// +// Serenity +////////////////////// +/datum/construction/mecha/serenity_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/serenity_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.overlays += I.icon_state+"+o" + qdel(I) + return 1 + +/datum/construction/mecha/serenity_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/serenity_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/serenity(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = 1 + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/serenity + result = "/obj/mecha/combat/gygax/serenity" + steps = list( + //1 + list("key"=/obj/item/weapon/weldingtool, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=/obj/item/weapon/weldingtool, + "desc"="Internal armor is welded."), + //4 + list("key"=/obj/item/weapon/weldingtool, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Medical module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Medical module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/medical, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=/obj/item/weapon/tool/wirecutters, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/serenity/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/serenity/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the medical control module into [holder].", "You install the medical control module into [holder].") + qdel(I) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the medical control module.", "You secure the medical control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the medical control module from [holder].", "You remove the medical control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/medical(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the medical control module.", "You unfasten the medical control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + var/obj/item/stack/material/steel/MS = new /obj/item/stack/material/steel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external armor layer to [holder].", "You install the external armor layer to [holder].") + qdel(I) + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external armor layer.") + holder.icon_state = "gygax19-s" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry the external armor layer from [holder].") + new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/serenity/spawn_result() + ..() + feedback_inc("mecha_serenity_created",1) + return + + + //////////////////////// -// Firfighter +// Firefighter //////////////////////// /datum/construction/mecha/firefighter_chassis steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 07c619b9ff..74cb1e169c 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -1,4 +1,4 @@ -///////////////////////// + ///////////////////////// ////// Mecha Parts ////// ///////////////////////// @@ -112,6 +112,14 @@ icon_state = "gygax_armour" origin_tech = list(TECH_MATERIAL = 6, TECH_COMBAT = 4, TECH_ENGINEERING = 5) +////////// Serenity + +/obj/item/mecha_parts/chassis/serenity + name = "Serenity Chassis" + + New() + ..() + construct = new /datum/construction/mecha/serenity_chassis(src) //////////// Durand diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm index 5a95b9017c..9894784e15 100644 --- a/code/game/mecha/mecha_wreckage.dm +++ b/code/game/mecha/mecha_wreckage.dm @@ -107,6 +107,10 @@ name = "Medgax wreckage" icon_state = "medgax-broken" +/obj/effect/decal/mecha_wreckage/gygax/serenity + name = "Serenity wreckage" + icon_state = "medgax-broken" + /obj/effect/decal/mecha_wreckage/marauder name = "Marauder wreckage" icon_state = "marauder-broken" @@ -217,3 +221,11 @@ name = "Janus wreckage" icon_state = "janus-broken" description_info = "Due to the incredibly intricate design of this exosuit, it is impossible to salvage components from it." + +/obj/effect/decal/mecha_wreckage/shuttlecraft + name = "Shuttlecraft wreckage" + desc = "Remains of some unfortunate shuttlecraft. Completely unrepairable." + icon = 'icons/mecha/mecha64x64.dmi' + icon_state = "shuttle_standard-broken" + bound_width = 64 + bound_height = 64 diff --git a/code/game/mecha/medical/medical.dm b/code/game/mecha/medical/medical.dm index 90ab62681a..6e9dee5047 100644 --- a/code/game/mecha/medical/medical.dm +++ b/code/game/mecha/medical/medical.dm @@ -5,6 +5,8 @@ max_universal_equip = 1 max_special_equip = 1 + stomp_sound = 'sound/mecha/mechmove01.ogg' + cargo_capacity = 1 /obj/mecha/medical/Initialize() @@ -13,7 +15,7 @@ if(isPlayerLevel(T.z)) new /obj/item/mecha_parts/mecha_tracking(src) - +/* // One horrific bastardization of glorious inheritence dead. A billion to go. ~Mech /obj/mecha/medical/mechturn(direction) set_dir(direction) playsound(src,'sound/mecha/mechmove01.ogg',40,1) @@ -29,4 +31,5 @@ var/result = step_rand(src) if(result) playsound(src,'sound/mecha/mechstep.ogg',25,1) - return result \ No newline at end of file + return result +*/ diff --git a/code/game/mecha/micro/mechfab_designs_vr.dm b/code/game/mecha/micro/mechfab_designs_vr.dm index 7a86d8d9ec..671e53b4da 100644 --- a/code/game/mecha/micro/mechfab_designs_vr.dm +++ b/code/game/mecha/micro/mechfab_designs_vr.dm @@ -185,3 +185,11 @@ id = "weasel_head" build_path = /obj/item/mecha_parts/micro/part/weasel_head materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 2500) + +/datum/design/item/mecha/medigun + name = "BL-3/P directed restoration system" + desc = "A portable medical system used to treat external injuries from afar." + id = "mech_medigun" + req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_BIO = 6) + materials = list(DEFAULT_WALL_MATERIAL = 8000, "gold" = 2000, "silver" = 1750, "diamond" = 1500, "phoron" = 4000) + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/medigun \ No newline at end of file diff --git a/code/game/mecha/micro/micro_equipment.dm b/code/game/mecha/micro/micro_equipment.dm index b8e4557b47..d36f9fa0a3 100644 --- a/code/game/mecha/micro/micro_equipment.dm +++ b/code/game/mecha/micro/micro_equipment.dm @@ -146,7 +146,7 @@ occupant_message("The ore compartment is full.") return 1 else - ore.Move(ore_box) + ore.forceMove(ore_box) else if(target.loc == C) log_message("Drilled through [target]") target.ex_act(2) diff --git a/code/game/mecha/working/hoverpod.dm b/code/game/mecha/space/hoverpod.dm similarity index 91% rename from code/game/mecha/working/hoverpod.dm rename to code/game/mecha/space/hoverpod.dm index 8c19c2b004..85fc59d564 100644 --- a/code/game/mecha/working/hoverpod.dm +++ b/code/game/mecha/space/hoverpod.dm @@ -17,6 +17,9 @@ var/datum/effect/effect/system/ion_trail_follow/ion_trail var/stabilization_enabled = 1 + stomp_sound = 'sound/machines/hiss.ogg' + swivel_sound = null + max_hull_equip = 2 max_weapon_equip = 0 max_utility_equip = 2 @@ -49,7 +52,7 @@ output += ..() return output -//No space drifting +// No space drifting /obj/mecha/working/hoverpod/check_for_support() //does the hoverpod have enough charge left to stabilize itself? if (!has_charge(step_energy_drain)) @@ -62,6 +65,11 @@ return ..() +// No falling if we've got our boosters on +/obj/mecha/working/hoverpod/can_fall() + return (stabilization_enabled && has_charge(step_energy_drain)) + +/* // One horrific bastardization of glorious inheritence dead. A billion to go. ~Mech //these three procs overriden to play different sounds /obj/mecha/working/hoverpod/mechturn(direction) set_dir(direction) @@ -80,7 +88,7 @@ if(result) playsound(src,'sound/machines/hiss.ogg',40,1) return result - +*/ //Hoverpod variants /obj/mecha/working/hoverpod/combatpod diff --git a/code/game/mecha/space/shuttle.dm b/code/game/mecha/space/shuttle.dm new file mode 100644 index 0000000000..7e361ece0c --- /dev/null +++ b/code/game/mecha/space/shuttle.dm @@ -0,0 +1,99 @@ +/obj/mecha/working/hoverpod/shuttlecraft + desc = "A more advanced variant of the hoverpod." + name = "Shuttle" + catalogue_data = list(/datum/category_item/catalogue/technology/hoverpod) + icon = 'icons/mecha/mecha64x64.dmi' + icon_state = "shuttle_standard" + initial_icon = "shuttle_standard" + internal_damage_threshold = 60 + step_in = 2 + step_energy_drain = 5 + max_temperature = 20000 + health = 300 + maxhealth = 300 + infra_luminosity = 6 + wreckage = /obj/effect/decal/mecha_wreckage/shuttlecraft + cargo_capacity = 3 + max_equip = 3 + + opacity = FALSE + + stomp_sound = 'sound/machines/generator/generator_end.ogg' + swivel_sound = 'sound/machines/hiss.ogg' + + // Paint colors! Null if not set. + var/base_paint + var/engine_paint + var/central_paint + var/front_paint + + var/image/base_paint_mask + var/image/engine_paint_mask + var/image/central_paint_mask + var/image/front_paint_mask + + bound_height = 64 + bound_width = 64 + + max_hull_equip = 2 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/hoverpod/Initialize() + ..() + ion_trail.stop() + +/obj/mecha/working/hoverpod/shuttlecraft/moved_inside(var/mob/living/carbon/human/H as mob) + . = ..(H) + if(.) + ion_trail.start() + +/obj/mecha/working/hoverpod/shuttlecraft/go_out() + . = ..() + if(!occupant) + ion_trail.stop() + +/obj/mecha/working/hoverpod/shuttlecraft/update_icon() + overlays.Cut() + ..() + + if(base_paint) + if(!base_paint_mask) + base_paint_mask = image(icon, "[initial_icon]-mask+base", src.layer + 1) + base_paint_mask.color = base_paint + overlays |= base_paint_mask + if(front_paint) + if(!front_paint_mask) + front_paint_mask = image(icon, "[initial_icon]-mask+front", src.layer + 1) + front_paint_mask.color = front_paint + overlays |= front_paint_mask + if(engine_paint) + if(!engine_paint_mask) + engine_paint_mask = image(icon, "[initial_icon]-mask+engine", src.layer + 1) + engine_paint_mask.color = engine_paint + overlays |= engine_paint_mask + if(central_paint) + if(!engine_paint_mask) + central_paint_mask = image(icon, "[initial_icon]-mask+central", src.layer + 2) + central_paint_mask.color = central_paint + overlays |= central_paint_mask + +/obj/mecha/working/hoverpod/shuttlecraft/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/multitool) && state == 1) + var/new_paint_location = input("Please select a target zone.", "Paint Zone", null) as null|anything in list("Central", "Engine", "Base", "Front", "CANCEL") + if(new_paint_location && new_paint_location != "CANCEL") + var/new_paint_color = input("Please select a paint color.", "Paint Color", null) as color|null + if(new_paint_color) + switch(new_paint_location) + if("Central") + central_paint = new_paint_color + if("Engine") + engine_paint = new_paint_color + if("Front") + front_paint = new_paint_color + if("Base") + base_paint = new_paint_color + update_icon() + else ..() diff --git a/code/game/objects/effects/alien/aliens.dm b/code/game/objects/effects/alien/aliens.dm index 44963c31d2..0de8d332b7 100644 --- a/code/game/objects/effects/alien/aliens.dm +++ b/code/game/objects/effects/alien/aliens.dm @@ -64,6 +64,19 @@ healthcheck() return +/obj/effect/alien/resin/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + user.do_attack_animation(src) + health -= damage + healthcheck() + return + +/obj/effect/alien/resin/take_damage(var/damage) + health -= damage + healthcheck() + return + /obj/effect/alien/resin/ex_act(severity) switch(severity) if(1.0) @@ -246,6 +259,18 @@ Alien plants should do something if theres a lot of poison health -= damage healthcheck() +/obj/effect/alien/weeds/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + user.do_attack_animation(src) + health -= damage + healthcheck() + return + +/obj/effect/alien/weeds/take_damage(var/damage) + health -= damage + healthcheck() + return + /obj/effect/alien/weeds/proc/healthcheck() if(health <= 0) qdel(src) @@ -401,6 +426,18 @@ Alien plants should do something if theres a lot of poison healthcheck() return +/obj/effect/alien/egg/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + user.do_attack_animation(src) + health -= damage + healthcheck() + return + +/obj/effect/alien/egg/take_damage(var/damage) + health -= damage + healthcheck() + return + /obj/effect/alien/egg/attackby(var/obj/item/weapon/W, var/mob/user) if(health <= 0) diff --git a/code/game/objects/effects/chem/coating.dm b/code/game/objects/effects/chem/coating.dm new file mode 100644 index 0000000000..a777bbe5e0 --- /dev/null +++ b/code/game/objects/effects/chem/coating.dm @@ -0,0 +1,36 @@ +/* + * Home of the floor chemical coating. + */ + +/obj/effect/decal/cleanable/chemcoating + icon = 'icons/effects/effects.dmi' + icon_state = "dirt" + +/obj/effect/decal/cleanable/chemcoating/New() + ..() + create_reagents(100) + +/obj/effect/decal/cleanable/chemcoating/Initialize() + ..() + var/turf/T = get_turf(src) + if(T) + for(var/obj/O in get_turf(src)) + if(O == src) + continue + if(istype(O, /obj/effect/decal/cleanable/chemcoating)) + var/obj/effect/decal/cleanable/chemcoating/C = O + if(C.reagents && C.reagents.reagent_list.len) + C.reagents.trans_to_obj(src,C.reagents.total_volume) + qdel(O) + +/obj/effect/decal/cleanable/chemcoating/Bumped(A as mob|obj) + if(reagents) + reagents.touch(A) + return ..() + +/obj/effect/decal/cleanable/chemcoating/Crossed(AM as mob|obj) + Bumped(AM) + +/obj/effect/decal/cleanable/chemcoating/update_icon() + ..() + color = reagents.get_color() diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index c71eec656f..6c50dc69b9 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -99,7 +99,6 @@ var/global/list/image/splatter_cache=list() S.overlays += S.blood_overlay if(S.blood_overlay && S.blood_overlay.color != basecolor) S.blood_overlay.color = basecolor - S.overlays.Cut() S.overlays += S.blood_overlay S.blood_DNA |= blood_DNA.Copy() perp.update_inv_shoes() diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index a827393dfb..d1de900bae 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -414,6 +414,10 @@ steam.start() -- spawns the effect src.processing = 0 spawn(0) var/turf/T = get_turf(src.holder) + if(istype(holder, /atom/movable)) + var/atom/movable/AM = holder + if(AM.locs && AM.locs.len) + T = pick(AM.locs) if(T != src.oldposition) if(isturf(T)) var/obj/effect/effect/ion_trails/I = new /obj/effect/effect/ion_trails(src.oldposition) diff --git a/code/game/objects/effects/map_effects/radiation_emitter.dm b/code/game/objects/effects/map_effects/radiation_emitter.dm index 3fb31d3c5d..8abf946556 100644 --- a/code/game/objects/effects/map_effects/radiation_emitter.dm +++ b/code/game/objects/effects/map_effects/radiation_emitter.dm @@ -1,19 +1,19 @@ -// Constantly emites radiation from the tile it's placed on. -/obj/effect/map_effect/radiation_emitter - name = "radiation emitter" - icon_state = "radiation_emitter" - var/radiation_power = 30 // Bigger numbers means more radiation. - -/obj/effect/map_effect/radiation_emitter/Initialize() - START_PROCESSING(SSobj, src) - return ..() - -/obj/effect/map_effect/radiation_emitter/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/map_effect/radiation_emitter/process() - radiation_repository.radiate(src, radiation_power) - +// Constantly emites radiation from the tile it's placed on. +/obj/effect/map_effect/radiation_emitter + name = "radiation emitter" + icon_state = "radiation_emitter" + var/radiation_power = 30 // Bigger numbers means more radiation. + +/obj/effect/map_effect/radiation_emitter/Initialize() + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/map_effect/radiation_emitter/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/map_effect/radiation_emitter/process() + SSradiation.radiate(src, radiation_power) + /obj/effect/map_effect/radiation_emitter/strong radiation_power = 100 \ No newline at end of file diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index f69cb901c2..870656d92e 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -6,6 +6,8 @@ invisibility = 99 // nope cant see this shit plane = ABOVE_PLANE anchored = 1 + icon = 'icons/mob/screen1.dmi' //VS Edit + icon_state = "centermarker" //VS Edit /obj/effect/step_trigger/proc/Trigger(var/atom/movable/A) return 0 @@ -233,6 +235,10 @@ var/global/list/tele_landmarks = list() // Terrible, but the alternative is loop if(isobserver(A)) A.forceMove(T) // Harmlessly move ghosts. return + //VOREStation Edit Start + if(!(A.can_fall())) + return // Phased shifted kin should not fall + //VOREStation Edit End A.forceMove(T) // Living things should probably be logged when they fall... diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 8a9220e87f..04046985c6 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -44,3 +44,14 @@ icon_state = "explosionfast" duration = 4 // VOREStation Add End + +//VOREStation edit: medigun +/obj/effect/temp_visual/heal + name = "healing glow" + icon_state = "heal" + duration = 15 + +/obj/effect/temp_visual/heal/Initialize(mapload) + pixel_x = rand(-12, 12) + pixel_y = rand(-9, 0) +//VOREStation edit ends \ No newline at end of file diff --git a/code/game/objects/effects/temporary_visuals/projectiles/impact.dm b/code/game/objects/effects/temporary_visuals/projectiles/impact.dm index 872c652356..d4f65fc9d1 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/impact.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/impact.dm @@ -79,3 +79,12 @@ light_range = 4 light_power = 3 light_color = "#3300ff" + +//VOREStation edit: medigun +/obj/effect/projectile/impact/medigun + icon = 'icons/obj/projectiles_vr.dmi' + icon_state = "impact_medbeam" + light_range = 2 + light_power = 0.5 + light_color = "#80F5FF" +//VOREStation edit ends \ No newline at end of file diff --git a/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm b/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm index 42511e6577..d901aeaa61 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm @@ -91,3 +91,12 @@ light_range = 4 light_power = 3 light_color = "#3300ff" + +//VOREStation edit: medigun +/obj/effect/projectile/muzzle/medigun + icon = 'icons/obj/projectiles_vr.dmi' + icon_state = "muzzle_medbeam" + light_range = 2 + light_power = 0.5 + light_color = "#80F5FF" +//VOREStation edit ends \ No newline at end of file diff --git a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm index 54fa41265f..59d56b6c7c 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm @@ -1,4 +1,12 @@ -/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! +/datum/beam_components_cache + var/list/beam_components = list() + +/datum/beam_components_cache/Destroy() + for(var/component in beam_components) + qdel(component) + return ..() + +/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, datum/beam_components_cache/beam_components, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! if(!istype(starting) || !istype(ending) || !ispath(beam_type)) return var/datum/point/midpoint = point_midpoint_points(starting, ending) @@ -21,10 +29,9 @@ for(var/obj/effect/projectile_lighting/PL in T) if(PL.owner == instance_key) continue tracing_line - QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key), qdel_in > 0? qdel_in : 5) + beam_components.beam_components += new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key) line = null - if(qdel_in) - QDEL_IN(PB, qdel_in) + beam_components.beam_components += PB /obj/effect/projectile/tracer name = "beam" @@ -107,3 +114,12 @@ light_range = 4 light_power = 3 light_color = "#3300ff" + +//VOREStation edit: medigun +/obj/effect/projectile/tracer/medigun + icon = 'icons/obj/projectiles_vr.dmi' + icon_state = "medbeam" + light_range = 2 + light_power = 0.5 + light_color = "#80F5FF" +//VOREStation edit ends \ No newline at end of file diff --git a/code/game/objects/effects/temporary_visuals/temproary_visual.dm~1fb83e6... Merge pull request #5959 from elgeonmb_suit++ b/code/game/objects/effects/temporary_visuals/temproary_visual.dm~1fb83e6... Merge pull request #5959 from elgeonmb_suit++ deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 3ba9c1bac7..b651e2bfe2 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -492,13 +492,15 @@ var/list/global/slot_flags_enumeration = list( user << "You cannot locate any eyes on [M]!" return - if(U.get_accuracy_penalty(U)) //Should only trigger if they're not aiming well - var/hit_zone = get_zone_with_miss_chance(U.zone_sel.selecting, M, U.get_accuracy_penalty(U)) - if(!hit_zone) - U.do_attack_animation(M) - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[U] attempts to stab [M] in the eyes, but misses!") - return + //this should absolutely trigger even if not aim-impaired in some way + var/hit_zone = get_zone_with_miss_chance(U.zone_sel.selecting, M, U.get_accuracy_penalty(U)) + if(!hit_zone) + U.do_attack_animation(M) + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + //visible_message("[U] attempts to stab [M] in the eyes, but misses!") + for(var/mob/V in viewers(M)) + V.show_message("[U] attempts to stab [M] in the eyes, but misses!") + return add_attack_logs(user,M,"Attack eyes with [name]") @@ -579,10 +581,9 @@ var/list/global/slot_flags_enumeration = list( if( !blood_overlay ) generate_blood_overlay() - //apply the blood-splatter overlay if it isn't already in there - if(!blood_DNA.len) - blood_overlay.color = blood_color - overlays += blood_overlay + //Make the blood_overlay have the proper color then apply it. + blood_overlay.color = blood_color + overlays += blood_overlay //if this blood isn't already in the list, add it if(istype(M)) @@ -591,6 +592,7 @@ var/list/global/slot_flags_enumeration = list( blood_DNA[M.dna.unique_enzymes] = M.dna.b_type return 1 //we applied blood to the item + /obj/item/proc/generate_blood_overlay() if(blood_overlay) return @@ -714,7 +716,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. icon = 'icons/obj/device.dmi' //Worn icon generation for on-mob sprites -/obj/item/proc/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer) +/obj/item/proc/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer,var/icon/clip_mask = null) //VOREStation edit - add 'clip mask' argument. //Get the required information about the base icon var/icon/icon2use = get_worn_icon_file(body_type = body_type, slot_name = slot_name, default_icon = default_icon, inhands = inhands) var/state2use = get_worn_icon_state(slot_name = slot_name) @@ -736,6 +738,8 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. if(!inhands) apply_custom(standing_icon) //Pre-image overridable proc to customize the thing apply_addblends(icon2use,standing_icon) //Some items have ICON_ADD blend shaders + if(istype(clip_mask)) //VOREStation Edit - For taur bodies/tails clipping off parts of uniforms and suits. + standing_icon = get_icon_difference(standing_icon, clip_mask, 1) var/image/standing = image(standing_icon) standing.alpha = alpha diff --git a/code/game/objects/items/bells.dm b/code/game/objects/items/bells.dm new file mode 100644 index 0000000000..c7aa08a440 --- /dev/null +++ b/code/game/objects/items/bells.dm @@ -0,0 +1,102 @@ +/obj/item/weapon/deskbell + name = "desk bell" + desc = "An annoying bell. Ring for service." + icon = 'icons/obj/items.dmi' + icon_state = "deskbell" + force = 2 + throwforce = 2 + w_class = 2.0 + var/broken + attack_verb = list("annoyed") + var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine") + var/static/radial_use = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_use") + var/static/radial_pickup = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_pickup") + +/obj/item/weapon/deskbell/examine(mob/user) + ..() + if(broken) + to_chat(user,"It looks damaged, the ringer is stuck firmly inside.") + +/obj/item/weapon/deskbell/attack(mob/target as mob, mob/living/user as mob) + if(!broken) + playsound(user.loc, 'sound/effects/deskbell.ogg', 50, 1) + ..() + +/obj/item/weapon/deskbell/attack_hand(mob/user) + + //This defines the radials and what call we're assiging to them. + var/list/options = list() + options["examine"] = radial_examine + options["pick up"] = radial_pickup + if(!broken) + options["use"] = radial_use + + + // Just an example, if the bell had no options, due to conditionals, nothing would happen here. + if(length(options) < 1) + return + + // Right, if there's only one available radial... + // For example, say, the bell's broken so you can only examine, it just does that (doesn't show radial).. + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + // If we have other options, it will show the radial menu for the player to decide. + choice = show_radial_menu(user, src, options, require_near = !issilicon(user)) + + // Once the player has decided their option, choose the behaviour that will happen under said option. + switch(choice) + if("examine") + examine(user) + + if("use") + if(check_ability(user)) + ring(user) + add_fingerprint(user) + + if("pick up") + ..() + +/obj/item/weapon/deskbell/proc/ring(mob/user) + if(user.a_intent == "harm") + playsound(user.loc, 'sound/effects/deskbell_rude.ogg', 50, 1) + to_chat(user,"You hammer [src] rudely!") + if (prob(2)) + break_bell(user) + else + playsound(user.loc, 'sound/effects/deskbell.ogg', 50, 1) + to_chat(user,"You gracefully ring [src].") + +/obj/item/weapon/deskbell/proc/check_ability(mob/user) + if (ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] + if (H.hand) + temp = H.organs_by_name["l_hand"] + if(temp && !temp.is_usable()) + to_chat(H,"You try to move your [temp.name], but cannot!") + return 0 + return 1 + else + to_chat(user,"You are not able to ring [src].") + return 0 + +/obj/item/weapon/deskbell/attackby(obj/item/W, mob/user, params) + if(!istype(W)) + return + if(W.is_wrench() && isturf(loc)) + if(do_after(5)) + if(!src) return + to_chat(user, "You dissasemble the desk bell") + new /obj/item/stack/material/steel(get_turf(src), 1) + qdel(src) + return + if(!broken) + ring(user) + + +/obj/item/weapon/deskbell/proc/break_bell(mob/user) + to_chat(user,"The ringing abruptly stops as [src]'s ringer gets jammed inside!") + broken = 1 diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index e8c7a0d08d..a6e1ef3c2d 100644 --- a/code/game/objects/items/contraband.dm +++ b/code/game/objects/items/contraband.dm @@ -4,30 +4,14 @@ /obj/item/weapon/storage/pill_bottle/happy name = "bottle of Happy pills" desc = "Highly illegal drug. When you want to see the rainbow." - -/obj/item/weapon/storage/pill_bottle/happy/New() - ..() - new /obj/item/weapon/reagent_containers/pill/happy( src ) - new /obj/item/weapon/reagent_containers/pill/happy( src ) - new /obj/item/weapon/reagent_containers/pill/happy( src ) - new /obj/item/weapon/reagent_containers/pill/happy( src ) - new /obj/item/weapon/reagent_containers/pill/happy( src ) - new /obj/item/weapon/reagent_containers/pill/happy( src ) - new /obj/item/weapon/reagent_containers/pill/happy( src ) + wrapper_color = COLOR_PINK + starts_with = list(/obj/item/weapon/reagent_containers/pill/happy = 7) /obj/item/weapon/storage/pill_bottle/zoom name = "bottle of Zoom pills" desc = "Highly illegal drug. Trade brain for speed." - -/obj/item/weapon/storage/pill_bottle/zoom/New() - ..() - new /obj/item/weapon/reagent_containers/pill/zoom( src ) - new /obj/item/weapon/reagent_containers/pill/zoom( src ) - new /obj/item/weapon/reagent_containers/pill/zoom( src ) - new /obj/item/weapon/reagent_containers/pill/zoom( src ) - new /obj/item/weapon/reagent_containers/pill/zoom( src ) - new /obj/item/weapon/reagent_containers/pill/zoom( src ) - new /obj/item/weapon/reagent_containers/pill/zoom( src ) + wrapper_color = COLOR_BLUE + starts_with = list(/obj/item/weapon/reagent_containers/pill/zoom = 7) /obj/item/weapon/reagent_containers/glass/beaker/vial/random flags = 0 diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index aef6825fdf..53a83c66bd 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -577,31 +577,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(mode==3) - var/turf/T = get_turf(user.loc) - if(!isnull(T)) - var/datum/gas_mixture/environment = T.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles - - if (total_moles) - var/o2_level = environment.gas["oxygen"]/total_moles - var/n2_level = environment.gas["nitrogen"]/total_moles - var/co2_level = environment.gas["carbon_dioxide"]/total_moles - var/phoron_level = environment.gas["phoron"]/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) - data["aircontents"] = list(\ - "pressure" = "[round(pressure,0.1)]",\ - "nitrogen" = "[round(n2_level*100,0.1)]",\ - "oxygen" = "[round(o2_level*100,0.1)]",\ - "carbon_dioxide" = "[round(co2_level*100,0.1)]",\ - "phoron" = "[round(phoron_level*100,0.01)]",\ - "other" = "[round(unknown_level, 0.01)]",\ - "temp" = "[round(environment.temperature-T0C,0.1)]",\ - "reading" = 1\ - ) - if(isnull(data["aircontents"])) - data["aircontents"] = list("reading" = 0) + data["aircontents"] = src.analyze_air() if(mode==6) if(has_reception) feeds.Cut() @@ -1120,7 +1096,7 @@ var/global/list/obj/item/device/pda/PDAs = list() P.conversations.Add("\ref[src]") - if (prob(15)) //Give the AI a chance of intercepting the message + if (prob(5) && security_level >= SEC_LEVEL_BLUE) //Give the AI a chance of intercepting the message //VOREStation Edit: no spam interception on lower codes + lower interception chance var/who = src.owner if(prob(50)) who = P.owner @@ -1550,3 +1526,37 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/emp_act(severity) for(var/atom/A in src) A.emp_act(severity) + +/obj/item/device/pda/proc/analyze_air() + var/list/results = list() + var/turf/T = get_turf(src.loc) + if(!isnull(T)) + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles + if (total_moles) + var/o2_level = environment.gas["oxygen"]/total_moles + var/n2_level = environment.gas["nitrogen"]/total_moles + var/co2_level = environment.gas["carbon_dioxide"]/total_moles + var/phoron_level = environment.gas["phoron"]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) + + // entry is what the element is describing + // Type identifies which unit or other special characters to use + // Val is the information reported + // Bad_high/_low are the values outside of which the entry reports as dangerous + // Poor_high/_low are the values outside of which the entry reports as unideal + // Values were extracted from the template itself + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + return results diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index ae4a3323dd..e34d1180c9 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -4,6 +4,7 @@ var/list/command_cartridges = list( /obj/item/weapon/cartridge/hos, /obj/item/weapon/cartridge/ce, /obj/item/weapon/cartridge/rd, + /obj/item/weapon/cartridge/cmo, /obj/item/weapon/cartridge/head, /obj/item/weapon/cartridge/lawyer // Internal Affaris, ) @@ -149,7 +150,7 @@ var/list/civilian_cartridges = list( */ /obj/item/weapon/cartridge/service - name = "\improper Serv-U Pro" + name = "\improper Serv-U Pro cartridge" desc = "A data cartridge designed to serve YOU!" /obj/item/weapon/cartridge/signal @@ -175,12 +176,12 @@ var/list/civilian_cartridges = list( access_quartermaster = 1 /obj/item/weapon/cartridge/miner - name = "\improper Drill-Jockey 4.5" + name = "\improper Drill-Jockey 4.5 cartridge" desc = "It's covered in some sort of sand." icon_state = "cart-q" /obj/item/weapon/cartridge/head - name = "\improper Easy-Record DELUXE" + name = "\improper Easy-Record DELUXE cartridge" icon_state = "cart-h" access_status_display = 1 @@ -193,7 +194,7 @@ var/list/civilian_cartridges = list( access_security = 1 /obj/item/weapon/cartridge/hos - name = "\improper R.O.B.U.S.T. DELUXE" + name = "\improper R.O.B.U.S.T. DELUXE cartridge" icon_state = "cart-hos" access_status_display = 1 access_security = 1 @@ -203,21 +204,21 @@ var/list/civilian_cartridges = list( . = ..() /obj/item/weapon/cartridge/ce - name = "\improper Power-On DELUXE" + name = "\improper Power-On DELUXE cartridge" icon_state = "cart-ce" access_status_display = 1 access_engine = 1 access_atmos = 1 /obj/item/weapon/cartridge/cmo - name = "\improper Med-U DELUXE" + name = "\improper Med-U DELUXE cartridge" icon_state = "cart-cmo" access_status_display = 1 access_reagent_scanner = 1 access_medical = 1 /obj/item/weapon/cartridge/rd - name = "\improper Signal Ace DELUXE" + name = "\improper Signal Ace DELUXE cartridge" icon_state = "cart-rd" access_status_display = 1 access_reagent_scanner = 1 diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm index 69f5bd0d4b..a4859c08a5 100644 --- a/code/game/objects/items/devices/communicator/UI.dm +++ b/code/game/objects/items/devices/communicator/UI.dm @@ -77,6 +77,8 @@ "Temperature" = planet.weather_holder.temperature - T0C, "High" = planet.weather_holder.current_weather.temp_high - T0C, "Low" = planet.weather_holder.current_weather.temp_low - T0C, + "WindDir" = planet.weather_holder.wind_dir ? dir2text(planet.weather_holder.wind_dir) : "None", + "WindSpeed" = planet.weather_holder.wind_speed ? "[planet.weather_holder.wind_speed > 2 ? "Severe" : "Normal"]" : "None", "Forecast" = english_list(planet.weather_holder.forecast, and_text = "→", comma_text = "→", final_comma_text = "→") // Unicode RIGHTWARDS ARROW. ) weather[++weather.len] = W @@ -114,7 +116,7 @@ data["flashlight"] = fon data["manifest"] = PDA_Manifest data["feeds"] = compile_news() - data["latest_news"] = get_recent_news() + //data["latest_news"] = get_recent_news() //VOREStation Edit, bandaid for catastrophic runtime lag in helper.dm if(cartridge) // If there's a cartridge, we need to grab the information from it data["cart_devices"] = cartridge.get_device_status() data["cart_templates"] = cartridge.ui_templates diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm index b7a3b752d9..273686fb46 100644 --- a/code/game/objects/items/devices/communicator/helper.dm +++ b/code/game/objects/items/devices/communicator/helper.dm @@ -19,17 +19,17 @@ // Poor_high/_low are the values outside of which the entry reports as unideal // Values were extracted from the template itself results = list( - list("entry" = "Pressure", "type" = "pressure", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), - list("entry" = "Temperature", "type" = "temp", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), - list("entry" = "Oxygen", "type" = "pressure", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), - list("entry" = "Nitrogen", "type" = "pressure", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), - list("entry" = "Carbon Dioxide", "type" = "pressure", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Phoron", "type" = "pressure", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Other", "type" = "pressure", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) ) if(isnull(results)) - results = list(list("entry" = "pressure", "val" = "0")) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) return results diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index 26fb1b69aa..b66c4a3776 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -610,12 +610,12 @@ return 1 /obj/item/weapon/shockpaddles/standalone/checked_use(var/charge_amt) - radiation_repository.radiate(src, charge_amt/12) //just a little bit of radiation. It's the price you pay for being powered by magic I guess + SSradiation.radiate(src, charge_amt/12) //just a little bit of radiation. It's the price you pay for being powered by magic I guess return 1 /obj/item/weapon/shockpaddles/standalone/process() if(fail_counter > 0) - radiation_repository.radiate(src, fail_counter--) + SSradiation.radiate(src, fail_counter--) else STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 1fd41ec768..acc89b39dc 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -122,9 +122,9 @@ if(use_external_power) battery = get_external_power_supply() - if(times_used <= max_flashes && battery && battery.checked_use(charge_cost)) + if(times_used <= max_flashes && battery && battery.charge >= charge_cost) last_used = world.time - if(prob( max(0, times_used - safe_flashes) * 2 + (times_used >= 1) ) && can_break) //if you use it 10 times in a minute it has a 30% chance to break. + if(prob( max(0, times_used - safe_flashes) * 2 + (times_used >= safe_flashes)) && can_break) //if you use it 10 times in a minute it has a 30% chance to break. broken = TRUE if(user) to_chat(user, "The bulb has burnt out!") diff --git a/code/game/objects/items/devices/geiger.dm b/code/game/objects/items/devices/geiger.dm index 76697ddba3..92ff449855 100644 --- a/code/game/objects/items/devices/geiger.dm +++ b/code/game/objects/items/devices/geiger.dm @@ -28,7 +28,7 @@ /obj/item/device/geiger/proc/get_radiation() if(!scanning) return - radiation_count = radiation_repository.get_rads_at_turf(get_turf(src)) + radiation_count = SSradiation.get_rads_at_turf(get_turf(src)) update_icon() update_sound() diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index 839dc7a2dc..830fa803e4 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -4,12 +4,12 @@ var/list/GPS_list = list() name = "global positioning system" desc = "Triangulates the approximate co-ordinates using a nearby satellite network. Alt+click to toggle power." icon = 'icons/obj/gps.dmi' - icon_state = "gps-c" + icon_state = "gps-gen" w_class = ITEMSIZE_TINY slot_flags = SLOT_BELT origin_tech = list(TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_MAGNET = 1) matter = list(DEFAULT_WALL_MATERIAL = 500) - var/gps_tag = "COM0" + var/gps_tag = "GEN0" var/emped = FALSE var/tracking = FALSE // Will not show other signals or emit its own signal if false. var/long_range = FALSE // If true, can see farther, depending on get_map_levels(). @@ -171,22 +171,43 @@ var/list/GPS_list = list() /obj/item/device/gps/on // Defaults to off to avoid polluting the signal list with a bunch of GPSes without owners. If you need to spawn active ones, use these. tracking = TRUE +/obj/item/device/gps/command + icon_state = "gps-com" + gps_tag = "COM0" + +/obj/item/device/gps/command/on + tracking = TRUE + +/obj/item/device/gps/security + icon_state = "gps-sec" + gps_tag = "SEC0" + +/obj/item/device/gps/security/on + tracking = TRUE + +/obj/item/device/gps/medical + icon_state = "gps-med" + gps_tag = "MED0" + +/obj/item/device/gps/medical/on + tracking = TRUE + /obj/item/device/gps/science - icon_state = "gps-s" + icon_state = "gps-sci" gps_tag = "SCI0" /obj/item/device/gps/science/on tracking = TRUE /obj/item/device/gps/engineering - icon_state = "gps-e" + icon_state = "gps-eng" gps_tag = "ENG0" /obj/item/device/gps/engineering/on tracking = TRUE /obj/item/device/gps/mining - icon_state = "gps-m" + icon_state = "gps-mine" gps_tag = "MINE0" desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life. Alt+click to toggle power." @@ -194,15 +215,15 @@ var/list/GPS_list = list() tracking = TRUE /obj/item/device/gps/explorer - icon_state = "gps-ex" - gps_tag = "EX0" + icon_state = "gps-exp" + gps_tag = "EXP0" desc = "A positioning system helpful for rescuing trapped or injured explorers, keeping one on you at all times while exploring might just save your life. Alt+click to toggle power." /obj/item/device/gps/explorer/on tracking = TRUE /obj/item/device/gps/robot - icon_state = "gps-b" + icon_state = "gps-borg" gps_tag = "SYNTH0" desc = "A synthetic internal positioning system. Used as a recovery beacon for damaged synthetic assets, or a collaboration tool for mining or exploration teams. \ Alt+click to toggle power." diff --git a/code/game/objects/items/devices/holowarrant.dm b/code/game/objects/items/devices/holowarrant.dm new file mode 100644 index 0000000000..36a13a6bec --- /dev/null +++ b/code/game/objects/items/devices/holowarrant.dm @@ -0,0 +1,110 @@ +/obj/item/device/holowarrant + name = "warrant projector" + desc = "The practical paperwork replacement for the officer on the go." + icon_state = "holowarrant" + item_state = "flashtool" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 10 + var/datum/data/record/warrant/active + +//look at it +/obj/item/device/holowarrant/examine(mob/user) + . = ..() + if(active) + to_chat(user, "It's a holographic warrant for '[active.fields["namewarrant"]]'.") + if(in_range(user, src) || istype(user, /mob/observer/dead)) + show_content(user) + else + to_chat(user, "You have to go closer if you want to read it.") + +//hit yourself with it +/obj/item/device/holowarrant/attack_self(mob/living/user as mob) + active = null + var/list/warrants = list() + if(!isnull(data_core.general)) + for(var/datum/data/record/warrant/W in data_core.warrants) + warrants += W.fields["namewarrant"] + if(warrants.len == 0) + to_chat(user,"There are no warrants available") + return + var/temp + temp = input(user, "Which warrant would you like to load?") as null|anything in warrants + for(var/datum/data/record/warrant/W in data_core.warrants) + if(W.fields["namewarrant"] == temp) + active = W + update_icon() + +/obj/item/device/holowarrant/attackby(obj/item/weapon/W, mob/user) + if(active) + var/obj/item/weapon/card/id/I = W.GetIdCard() + if(I) + var/choice = alert(user, "Would you like to authorize this warrant?","Warrant authorization","Yes","No") + if(choice == "Yes") + active.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]" + user.visible_message("You swipe \the [I] through the [src].", \ + "[user] swipes \the [I] through the [src].") + return 1 + ..() + +//hit other people with it +/obj/item/device/holowarrant/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + user.visible_message("You show the warrant to [M].", \ + "[user] holds up a warrant projector and shows the contents to [M].") + M.examinate(src) + +/obj/item/device/holowarrant/update_icon() + if(active) + icon_state = "holowarrant_filled" + else + icon_state = "holowarrant" + +/obj/item/device/holowarrant/proc/show_content(mob/user, forceshow) + if(!active) + return + if(active.fields["arrestsearch"] == "arrest") + var/output = {" + [active.fields["namewarrant"]] +
    Sol Central Government Colonial Marshal Bureau
    + in the jurisdiction of the
    + [using_map.boss_name] in [using_map.station_name]
    +
    + ARREST WARRANT

    +
    + This document serves as authorization and notice for the arrest of _[active.fields["namewarrant"]]____ for the crime(s) of:
    [active.fields["charges"]]
    +
    + Vessel or habitat: _[using_map.station_name]____
    +
    _[active.fields["auth"]]____
    + Person authorizing arrest
    + + "} + + show_browser(user, output, "window=Warrant for the arrest of [active.fields["namewarrant"]]") + if(active.fields["arrestsearch"] == "search") + var/output= {" + Search Warrant: [active.fields["namewarrant"]] +
    in the jurisdiction of the
    + [using_map.boss_name] in [using_map.station_name]
    +
    + SEARCH WARRANT

    +
    + The Security Officer(s) bearing this Warrant are hereby authorized by the Issuer
    + to conduct a one time lawful search of the Suspect's person/belongings/premises and/or Department
    + for any items and materials that could be connected to the suspected criminal act described below,
    + pending an investigation in progress. The Security Officer(s) are obligated to remove any and all
    + such items from the Suspects posession and/or Department and file it as evidence. The Suspect/Department
    + staff is expected to offer full co-operation. In the event of the Suspect/Department staff attempting
    + to resist/impede this search or flee, they must be taken into custody immediately!
    + All confiscated items must be filed and taken to Evidence!

    +
    + Suspect's/location name: [active.fields["namewarrant"]]
    +
    + For the following reasons: [active.fields["charges"]]
    +
    + Warrant issued by: [active.fields ["auth"]]
    +
    + Vessel or habitat: _[using_map.station_name]____
    + + "} + show_browser(user, output, "window=Search warrant for [active.fields["namewarrant"]]") \ No newline at end of file diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 7e2b054165..4686aee807 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -121,48 +121,18 @@ /obj/item/device/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U) - if(target.status != LIGHT_OK) - if(CanUse(U)) - if(!Use(U)) return - U << "You replace the [target.fitting] with the [src]." + if(target.status == LIGHT_OK) + to_chat(U, "There is a working [target.get_fitting_name()] already inserted.") + else if(!CanUse(U)) + to_chat(U, failmsg) + else if(Use(U)) + to_chat(U, "You replace the [target.get_fitting_name()] with the [src].") - if(target.status != LIGHT_EMPTY) + if(target.status != LIGHT_EMPTY) + target.remove_bulb() - var/obj/item/weapon/light/L1 = new target.light_type(target.loc) - L1.status = target.status - L1.rigged = target.rigged - L1.brightness_range = target.brightness_range - L1.brightness_power = target.brightness_power - L1.brightness_color = target.brightness_color - L1.switchcount = target.switchcount - target.switchcount = 0 - L1.update() - - target.status = LIGHT_EMPTY - target.update() - - var/obj/item/weapon/light/L2 = new target.light_type() - - target.status = L2.status - target.switchcount = L2.switchcount - target.rigged = emagged - target.brightness_range = L2.brightness_range - target.brightness_power = L2.brightness_power - target.brightness_color = L2.brightness_color - target.on = target.has_power() - target.update() - qdel(L2) - - if(target.on && target.rigged) - target.explode() - return - - else - U << failmsg - return - else - U << "There is a working [target.fitting] already inserted." - return + var/obj/item/weapon/light/L = new target.light_type() + target.insert_bulb(L) /obj/item/device/lightreplacer/emag_act(var/remaining_charges, var/mob/user) emagged = !emagged diff --git a/code/game/objects/items/devices/radio/encryptionkey_vr.dm b/code/game/objects/items/devices/radio/encryptionkey_vr.dm index 4d2debc341..a70ad6f7df 100644 --- a/code/game/objects/items/devices/radio/encryptionkey_vr.dm +++ b/code/game/objects/items/devices/radio/encryptionkey_vr.dm @@ -18,3 +18,9 @@ name = "research director's encryption key" icon_state = "rd_cypherkey" channels = list("Command" = 1, "Science" = 1, "Explorer" = 1) + +/obj/item/device/encryptionkey/ert + channels = list("Response Team" = 1, "Science" = 1, "Command" = 1, "Medical" = 1, "Engineering" = 1, "Security" = 1, "Supply" = 1, "Service" = 1, "Explorer" = 1) + +/obj/item/device/encryptionkey/omni //Literally only for the admin intercoms + channels = list("Mercenary" = 1, "Raider" = 1, "Response Team" = 1, "Science" = 1, "Command" = 1, "Medical" = 1, "Engineering" = 1, "Security" = 1, "Supply" = 1, "Service" = 1, "Explorer" = 1) diff --git a/code/game/objects/items/devices/radio/headset_vr.dm b/code/game/objects/items/devices/radio/headset_vr.dm index 46d4ead3d8..71a37ac0d7 100644 --- a/code/game/objects/items/devices/radio/headset_vr.dm +++ b/code/game/objects/items/devices/radio/headset_vr.dm @@ -3,6 +3,7 @@ desc = "The headset of the boss's boss." icon_state = "cent_headset" item_state = "headset" + centComm = 1 ks2type = /obj/item/device/encryptionkey/ert /obj/item/device/radio/headset/centcom/alt @@ -13,5 +14,9 @@ name = "\improper NT radio headset" desc = "The headset of a Nanotrasen corporate employee." icon_state = "nt_headset" + centComm = 1 ks2type = /obj/item/device/encryptionkey/ert +/obj/item/device/radio/headset + sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/seromi/ears.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/ears.dmi') \ No newline at end of file diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 971eb26a46..747f7f7d0e 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -95,7 +95,7 @@ var/global/list/default_medbay_channels = list( /obj/item/device/radio/interact(mob/user) if(!user) - return 0 + return FALSE if(b_stat) wires.Interact(user) @@ -151,10 +151,10 @@ var/global/list/default_medbay_channels = list( /obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq) if(!user) - return 0 + return FALSE if(!(freq in internal_channels)) - return 0 + return FALSE return user.has_internal_radio_channel_access(internal_channels[freq]) @@ -191,7 +191,7 @@ var/global/list/default_medbay_channels = list( /obj/item/device/radio/Topic(href, href_list) if(..()) - return 1 + return TRUE usr.set_machine(src) if (href_list["track"]) @@ -229,7 +229,7 @@ var/global/list/default_medbay_channels = list( set_frequency(text2num(freq)) . = 1 if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk - return 1 + return TRUE if(.) SSnanoui.update_uis(src) @@ -246,8 +246,6 @@ var/global/list/default_medbay_channels = list( channel = null if (!istype(connection)) return - if (!connection) - return var/static/mob/living/silicon/ai/announcer/A = new /mob/living/silicon/ai/announcer(src, null, null, 1) A.SetName(from) @@ -274,18 +272,18 @@ var/global/list/default_medbay_channels = list( return null /obj/item/device/radio/talk_into(mob/living/M as mob, message, channel, var/verb = "says", var/datum/language/speaking = null) - if(!on) return 0 // the device has to be on + if(!on) return FALSE // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. - if(!M || !message) return 0 + if(!M || !message) return FALSE - if(speaking && (speaking.flags & (SIGNLANG|NONVERBAL))) return 0 + if(speaking && (speaking.flags & (SIGNLANG|NONVERBAL))) return FALSE if(istype(M)) M.trigger_aiming(TARGET_CAN_RADIO) // Uncommenting this. To the above comment: // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom if(wires.IsIndexCut(WIRE_TRANSMIT)) // The device has to have all its wires and shit intact - return 0 + return FALSE if(!radio_connection) set_frequency(frequency) @@ -304,9 +302,7 @@ var/global/list/default_medbay_channels = list( //#### Grab the connection datum ####// var/datum/radio_frequency/connection = handle_message_mode(M, message, channel) if (!istype(connection)) - return 0 - if (!connection) - return 0 + return FALSE var/turf/position = get_turf(src) @@ -360,13 +356,12 @@ var/global/list/default_medbay_channels = list( /* ###### Radio headsets can only broadcast through subspace ###### */ - if(subspace_transmission) var/list/jamming = is_jammed(src) if(jamming) var/distance = jamming["distance"] to_chat(M,"\icon[src] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") - return 0 + return FALSE // First, we want to generate a new radio signal var/datum/signal/signal = new @@ -414,6 +409,7 @@ var/global/list/default_medbay_channels = list( for(var/obj/machinery/telecomms/allinone/R in telecomms_list) R.receive_signal(signal) + // Receiving code can be located in Telecommunications.dm if(signal.data["done"] && position.z in signal.data["level"]) return TRUE //Huzzah, sent via subspace @@ -422,7 +418,7 @@ var/global/list/default_medbay_channels = list( subspace_transmission = FALSE return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), src, message, displayname, jobname, real_name, M.voice_name, - signal.transmission_method, signal.data["compression"], list(position.z), connection.frequency,verb,speaking) + signal.transmission_method, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb,speaking) /* ###### Intercoms and station-bounced radios ###### */ @@ -474,13 +470,13 @@ var/global/list/default_medbay_channels = list( to_chat(loc,"\The [src] pings as it reestablishes subspace communications.") subspace_transmission = TRUE // we're done here. - return 1 + return TRUE // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. // Send a mundane broadcast with limited targets: //THIS IS TEMPORARY. YEAH RIGHT - if(!connection) return 0 //~Carn + if(!connection) return FALSE //~Carn //VOREStation Add Start if(bluespace_radio) @@ -491,7 +487,7 @@ var/global/list/default_medbay_channels = list( return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), src, message, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], list(position.z), connection.frequency,verb,speaking) + filter_type, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb,speaking) /obj/item/device/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null) @@ -504,7 +500,7 @@ var/global/list/default_medbay_channels = list( /obj/item/device/radio/proc/accept_rad(obj/item/device/radio/R as obj, message) if ((R.frequency == frequency && message)) - return 1 + return TRUE else if else @@ -526,9 +522,8 @@ var/global/list/default_medbay_channels = list( return -1 if(!(0 in level)) var/turf/position = get_turf(src) - if(!position || !(position.z in level)) - if(!bluespace_radio) //VOREStation Edit - return -1 + if((!position || !(position.z in level)) && !bluespace_radio) //VOREStation Edit + return -1 if(freq in ANTAG_FREQS) if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys return -1 @@ -545,7 +540,7 @@ var/global/list/default_medbay_channels = list( if (!accept) for (var/ch_name in channels) var/datum/radio_frequency/RF = secure_radio_connections[ch_name] - if (RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) + if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) accept = 1 break if (!accept) @@ -696,7 +691,7 @@ var/global/list/default_medbay_channels = list( /obj/item/device/radio/borg/Topic(href, href_list) if(..()) - return 1 + return TRUE if (href_list["mode"]) var/enable_subspace_transmission = text2num(href_list["mode"]) if(enable_subspace_transmission != subspace_transmission) diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm index 26684ce704..86a16280fc 100644 --- a/code/game/objects/items/devices/radio/radio_vr.dm +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -56,14 +56,14 @@ ..() /obj/item/device/subspaceradio/MouseDrop() - if(ismob(loc)) - if(!CanMouseDrop(src)) - return - var/mob/M = loc - if(!M.unEquip(src)) - return - add_fingerprint(usr) - M.put_in_any_hand_if_possible(src) + if(ismob(loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = loc + if(!M.unEquip(src)) + return + add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) /obj/item/device/subspaceradio/attackby(obj/item/weapon/W, mob/user, params) if(W == handset) @@ -99,7 +99,7 @@ if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) return 1 - if((slot_flags & SLOT_BELT) && M.get_equipped_item(slot_belt) == src) + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src) return 1 return 0 @@ -114,7 +114,7 @@ if(ismob(handset.loc)) var/mob/M = handset.loc if(M.drop_from_inventory(handset, src)) - to_chat(user, "\The [handset] snap back into the main unit.") + to_chat(user, "\The [handset] snaps back into the main unit.") else handset.forceMove(src) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index b155a1fb54..a085e59d24 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -1,3 +1,4 @@ +#define DEFIB_TIME_LIMIT (10 MINUTES) //VOREStation addition- past this many seconds, defib is useless. /* CONTAINS: T-RAY @@ -54,6 +55,8 @@ HALOGEN COUNTER - Radcount on mobs if (!(ishuman(user) || ticker) && ticker.mode.name != "monkey") to_chat(user, "You don't have the dexterity to do this!") return + + flick("[icon_state]-scan", src) //makes it so that it plays the scan animation on a succesful scan user.visible_message("[user] has analyzed [M]'s vitals.","You have analyzed [M]'s vitals.") if (!ishuman(M) || M.isSynthetic()) @@ -81,8 +84,13 @@ HALOGEN COUNTER - Radcount on mobs dat += "\tKey: Suffocation/Toxin/Burns/Brute
    " dat += "\tDamage Specifics: [OX] - [TX] - [BU] - [BR]
    " dat += "Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)
    " - if(M.tod && (M.stat == DEAD || (M.status_flags & FAKEDEATH))) - dat += "Time of Death: [M.tod]
    " + //VOREStation edit/addition starts + if(M.timeofdeath && (M.stat == DEAD || (M.status_flags & FAKEDEATH))) + dat += "Time of Death: [worldtime2stationtime(M.timeofdeath)]
    " + var/tdelta = round(world.time - M.timeofdeath) + if(tdelta < (DEFIB_TIME_LIMIT * 10)) + dat += "Subject died [DisplayTimeText(tdelta)] ago - resuscitation may be possible!
    " + //VOREStation edit/addition ends if(istype(M, /mob/living/carbon/human) && mode == 1) var/mob/living/carbon/human/H = M var/list/damaged = H.get_damaged_organs(1,1) @@ -167,6 +175,30 @@ HALOGEN COUNTER - Radcount on mobs dat += stomachunknownreagents[d] else dat += "Unknown substance[(unknown > 1)?"s":""] found in subject's stomach.
    " + if(C.touching && C.touching.total_volume) + var/unknown = 0 + var/touchreagentdata[0] + var/touchunknownreagents[0] + for(var/B in C.touching.reagent_list) + var/datum/reagent/T = B + if(T.scannable) + touchreagentdata["[T.id]"] = "\t[round(C.touching.get_reagent_amount(T.id), 1)]u [T.name]
    " + if (advscan == 0 || showadvscan == 0) + dat += "[T.name] found in subject's dermis.
    " + else + ++unknown + touchunknownreagents["[T.id]"] = "\t[round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]
    " + if(advscan >= 1 && showadvscan == 1) + dat += "Beneficial reagents detected in subject's dermis:
    " + for(var/d in touchreagentdata) + dat += touchreagentdata[d] + if(unknown) + if(advscan >= 3 && showadvscan == 1) + dat += "Warning: Non-medical reagent[(unknown > 1)?"s":""] found in subject's dermis:
    " + for(var/d in touchunknownreagents) + dat += touchunknownreagents[d] + else + dat += "Unknown substance[(unknown > 1)?"s":""] found in subject's dermis.
    " if(C.virus2.len) for (var/ID in C.virus2) if (ID in virusDB) @@ -211,7 +243,7 @@ HALOGEN COUNTER - Radcount on mobs continue // Broken limbs if(e.status & ORGAN_BROKEN) - if((e.name in list("l_arm", "r_arm", "l_leg", "r_leg")) && (!e.splinted)) + if((e.name in list("l_arm", "r_arm", "l_leg", "r_leg", "head", "chest", "groin")) && (!e.splinted)) fracture_dat += "Unsecured fracture in subject [e.name]. Splinting recommended for transport.
    " else if(advscan >= 1 && showadvscan == 1) fracture_dat += "Bone fractures detected in subject [e.name].
    " @@ -501,3 +533,5 @@ HALOGEN COUNTER - Radcount on mobs else to_chat(user, "No radiation detected.") return + +#undef DEFIB_TIME_LIMIT //VOREStation addition \ No newline at end of file diff --git a/code/game/objects/items/devices/uplink_random_lists.dm b/code/game/objects/items/devices/uplink_random_lists.dm index 38605da018..23dc77d143 100644 --- a/code/game/objects/items/devices/uplink_random_lists.dm +++ b/code/game/objects/items/devices/uplink_random_lists.dm @@ -74,7 +74,6 @@ var/datum/uplink_random_selection/all_uplink_selection = new/datum/uplink_random items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_kit) items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_projector) items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/voice) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/camera_floppy, 10, 0) items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/heavy_vest) items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/combat) diff --git a/code/game/objects/items/glassjar.dm b/code/game/objects/items/glassjar.dm index 2e73e89e67..ce5bfbe91e 100644 --- a/code/game/objects/items/glassjar.dm +++ b/code/game/objects/items/glassjar.dm @@ -6,7 +6,7 @@ w_class = ITEMSIZE_SMALL matter = list("glass" = 200) flags = NOBLUDGEON - var/list/accept_mobs = list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/mouse) + var/list/accept_mobs = list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/mouse, /mob/living/simple_mob/animal/sif/leech, /mob/living/simple_mob/animal/sif/frostfly, /mob/living/simple_mob/animal/sif/glitterfly) var/contains = 0 // 0 = nothing, 1 = money, 2 = animal, 3 = spiderling /obj/item/glass_jar/New() @@ -96,6 +96,12 @@ for(var/mob/M in src) var/image/victim = image(M.icon, M.icon_state) victim.pixel_y = 6 + victim.color = M.color + if(M.plane == PLANE_LIGHTING_ABOVE) // This will only show up on the ground sprite, due to the HuD being over it, so we need both images. + var/image/victim_glow = image(M.icon, M.icon_state) + victim_glow.pixel_y = 6 + victim_glow.color = M.color + underlays += victim_glow underlays += victim name = "glass jar with [M]" desc = "A small jar with [M] inside." diff --git a/code/game/objects/items/poi_items.dm b/code/game/objects/items/poi_items.dm index 6fd6d7debd..c12a3a655a 100644 --- a/code/game/objects/items/poi_items.dm +++ b/code/game/objects/items/poi_items.dm @@ -13,7 +13,7 @@ return ..() /obj/item/poi/pascalb/process() - radiation_repository.radiate(src, 5) + SSradiation.radiate(src, 5) /obj/item/poi/pascalb/Destroy() STOP_PROCESSING(SSobj, src) @@ -41,7 +41,7 @@ return ..() /obj/item/poi/brokenoldreactor/process() - radiation_repository.radiate(src, 25) + SSradiation.radiate(src, 25) /obj/item/poi/brokenoldreactor/Destroy() STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/robot/robot_upgrades_vr.dm b/code/game/objects/items/robot/robot_upgrades_vr.dm index a3c7709944..13f09a980d 100644 --- a/code/game/objects/items/robot/robot_upgrades_vr.dm +++ b/code/game/objects/items/robot/robot_upgrades_vr.dm @@ -6,6 +6,7 @@ R.add_language(LANGUAGE_ECUREUILIAN, 1) R.add_language(LANGUAGE_DAEMON, 1) R.add_language(LANGUAGE_ENOCHIAN, 1) + R.add_language(LANGUAGE_SLAVIC, 1) return 1 else return 0 diff --git a/code/game/objects/items/stacks/matter_synth.dm b/code/game/objects/items/stacks/matter_synth.dm index 3483dfbc61..d92cd5f8dd 100644 --- a/code/game/objects/items/stacks/matter_synth.dm +++ b/code/game/objects/items/stacks/matter_synth.dm @@ -50,4 +50,9 @@ /datum/matter_synth/wire name = "Wire Synthesizer" max_energy = 50 - recharge_rate = 2 \ No newline at end of file + recharge_rate = 2 + +/datum/matter_synth/bandage + name = "Bandage Synthesizer" + max_energy = 10 + recharge_rate = 1 diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index fe09f30410..51e2b8a03e 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -11,6 +11,8 @@ var/heal_burn = 0 var/apply_sounds + var/upgrade_to // The type path this stack can be upgraded to. + /obj/item/stack/medical/attack(mob/living/carbon/M as mob, mob/user as mob) if (!istype(M)) user << "\The [src] cannot be applied to [M]!" @@ -59,6 +61,80 @@ use(1) M.updatehealth() + +/obj/item/stack/medical/proc/upgrade_stack(var/upgrade_amount) + . = FALSE + + var/turf/T = get_turf(src) + + if(ispath(upgrade_to) && use(upgrade_amount)) + var/obj/item/stack/medical/M = new upgrade_to(T, upgrade_amount) + return M + + return . + +/obj/item/stack/medical/crude_pack + name = "crude bandage" + singular_name = "crude bandage length" + desc = "Some bandages to wrap around bloody stumps." + icon_state = "gauze" + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + + upgrade_to = /obj/item/stack/medical/bruise_pack + +/obj/item/stack/medical/crude_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + else + user.visible_message("\The [user] starts bandaging [M]'s [affecting.name].", \ + "You start bandaging [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if (W.internal) + continue + if(W.bandaged) + continue + if(used == amount) + break + if(!do_mob(user, M, W.damage/3)) + to_chat(user, "You must stand still to bandage wounds.") + break + + if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ + "You bandage \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] places a bandage over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bandage over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + playsound(src, pick(apply_sounds), 25) + used++ + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + /obj/item/stack/medical/bruise_pack name = "roll of gauze" singular_name = "gauze length" @@ -68,6 +144,8 @@ no_variants = FALSE apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + upgrade_to = /obj/item/stack/medical/advanced/bruise_pack + /obj/item/stack/medical/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) if(..()) return 1 @@ -220,6 +298,7 @@ W.heal_damage(heal_brute) playsound(src, pick(apply_sounds), 25) used = 1 //VOREStation Edit + update_icon() // VOREStation Edit - Support for stack icons affecting.update_damages() if(used == amount) if(affecting.is_bandaged()) @@ -266,16 +345,17 @@ use(1) affecting.salve() playsound(src, pick(apply_sounds), 25) + update_icon() // VOREStation Edit - Support for stack icons /obj/item/stack/medical/splint name = "medical splints" singular_name = "medical splint" - desc = "Modular splints capable of supporting and immobilizing bones in both limbs and appendages." + desc = "Modular splints capable of supporting and immobilizing bones in all areas of the body." icon_state = "splint" amount = 5 max_amount = 5 - var/list/splintable_organs = list(BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT) //List of organs you can splint, natch. + var/list/splintable_organs = list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO) //List of organs you can splint, natch. /obj/item/stack/medical/splint/attack(mob/living/carbon/M as mob, mob/living/user as mob) if(..()) diff --git a/code/game/objects/items/stacks/medical_vr.dm b/code/game/objects/items/stacks/medical_vr.dm new file mode 100644 index 0000000000..eda72a3fce --- /dev/null +++ b/code/game/objects/items/stacks/medical_vr.dm @@ -0,0 +1,21 @@ +/obj/item/stack/medical/advanced + icon = 'icons/obj/stacks_vr.dmi' + +/obj/item/stack/medical/advanced/Initialize() + . = ..() + update_icon() + +/obj/item/stack/medical/advanced/update_icon() + switch(amount) + if(1 to 2) + icon_state = initial(icon_state) + if(3 to 4) + icon_state = "[initial(icon_state)]_4" + if(5 to 6) + icon_state = "[initial(icon_state)]_6" + if(7 to 8) + icon_state = "[initial(icon_state)]_8" + if(9) + icon_state = "[initial(icon_state)]_9" + else + icon_state = "[initial(icon_state)]_10" \ No newline at end of file diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm index 7b156c025f..1170fedc04 100644 --- a/code/game/objects/items/stacks/nanopaste.dm +++ b/code/game/objects/items/stacks/nanopaste.dm @@ -29,18 +29,21 @@ if (istype(M,/mob/living/carbon/human)) //Repairing robolimbs var/mob/living/carbon/human/H = M var/obj/item/organ/external/S = H.get_organ(user.zone_sel.selecting) - + //VOREStation Edit Start if (S && (S.robotic >= ORGAN_ROBOT)) if(!S.get_damage()) - user << "Nothing to fix here." + to_chat(user, "Nothing to fix here.") + else if((S.open < 2) && (S.brute_dam + S.burn_dam >= S.min_broken_damage) && !repair_external) + to_chat(user, "The damage is too extensive for this nanite swarm to handle.") else if(can_use(1)) user.setClickCooldown(user.get_attack_speed(src)) if(S.open >= 2) if(do_after(user,5 * toolspeed)) - S.heal_damage(20, 20, robo_repair = 1) + S.heal_damage(restoration_internal, restoration_internal, robo_repair = 1) else if(do_after(user,5 * toolspeed)) - S.heal_damage(10,10, robo_repair =1) + S.heal_damage(restoration_external,restoration_external, robo_repair =1) H.updatehealth() use(1) user.visible_message("\The [user] applies some nanite paste on [user != M ? "[M]'s [S.name]" : "[S]"] with [src].",\ "You apply some nanite paste on [user == M ? "your" : "[M]'s"] [S.name].") + //VOREStation Edit End diff --git a/code/game/objects/items/stacks/nanopaste_vr.dm b/code/game/objects/items/stacks/nanopaste_vr.dm new file mode 100644 index 0000000000..cd39b61fef --- /dev/null +++ b/code/game/objects/items/stacks/nanopaste_vr.dm @@ -0,0 +1,13 @@ +/obj/item/stack/nanopaste + var/restoration_external = 5 + var/restoration_internal = 20 + var/repair_external = FALSE + +/obj/item/stack/nanopaste/advanced + name = "advanced nanopaste" + singular_name = "advanced nanite swarm" + desc = "A tube of paste containing swarms of repair nanites. Very effective in repairing robotic machinery. These ones are capable of restoring condition even of most thrashed robotic parts" + icon = 'icons/obj/stacks_vr.dmi' + icon_state = "adv_nanopaste" + restoration_external = 10 + repair_external = TRUE \ No newline at end of file diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index c5be255204..984c5b97f6 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -24,6 +24,9 @@ var/list/datum/matter_synth/synths = null var/no_variants = TRUE // Determines whether the item should update it's sprites based on amount. + var/pass_color = FALSE // Will the item pass its own color var to the created item? Dyed cloth, wood, etc. + var/strict_color_stacking = FALSE // Will the stack merge with other stacks that are different colors? (Dyed cloth, wood, etc) + /obj/item/stack/New(var/loc, var/amount=null) ..() if (!stacktype) @@ -159,6 +162,17 @@ for (var/obj/item/I in O) qdel(I) + if ((pass_color || recipe.pass_color)) + if(!color) + if(recipe.use_material) + var/material/MAT = get_material_by_name(recipe.use_material) + if(MAT.icon_colour) + O.color = MAT.icon_colour + else + return + else + O.color = color + /obj/item/stack/Topic(href, href_list) ..() if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) @@ -242,6 +256,9 @@ return 0 if ((stacktype != S.stacktype) && !type_verified) return 0 + if ((strict_color_stacking || S.strict_color_stacking) && S.color != color) + return 0 + if (isnull(tamount)) tamount = src.get_amount() @@ -355,8 +372,9 @@ var/one_per_turf = 0 var/on_floor = 0 var/use_material + var/pass_color - New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, supplied_material = null) + New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, supplied_material = null, pass_stack_color) src.title = title src.result_type = result_type src.req_amount = req_amount @@ -366,6 +384,7 @@ src.one_per_turf = one_per_turf src.on_floor = on_floor src.use_material = supplied_material + src.pass_color = pass_stack_color /* * Recipe list datum diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 93c70930d1..7c441e8169 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -202,4 +202,12 @@ name = "roofing" singular_name = "roofing" desc = "A section of roofing material. You can use it to repair the ceiling, or expand it." - icon_state = "techtile_grid" \ No newline at end of file + icon_state = "techtile_grid" + +/obj/item/stack/tile/roofing/cyborg + name = "roofing synthesizer" + desc = "A device that makes roofing tiles." + uses_charge = 1 + charge_costs = list(250) + stacktype = /obj/item/stack/tile/roofing + build_type = /obj/item/stack/tile/roofing \ No newline at end of file diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 92faf9e295..dd7b87184c 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -935,6 +935,12 @@ icon_state = "nymphplushie" pokephrase = "Chirp!" +/obj/item/toy/plushie/teshari + name = "teshari plush" + desc = "This is a plush teshari. Very soft, with a pompom on the tail. The toy is made well, as if alive. Looks like she is sleeping. Shhh!" + icon_state = "teshariplushie" + pokephrase = "Rya!" + /obj/item/toy/plushie/mouse name = "mouse plush" desc = "A plushie of a delightful mouse! What was once considered a vile rodent is now your very best friend." @@ -1363,4 +1369,4 @@ icon_state = "tinyxmastree" w_class = ITEMSIZE_TINY force = 1 - throwforce = 1 \ No newline at end of file + throwforce = 1 diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 3d9c4ea140..12315972a2 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -76,6 +76,14 @@ name = "\improper \"LiquidFood\" ration" icon_state = "liquidfood" +/obj/item/trash/liquidprotein + name = "\improper \"LiquidProtein\" ration" + icon_state = "liquidprotein" + +/obj/item/trash/liquidvitamin + name = "\improper \"VitaPaste\" ration" + icon_state = "liquidvitamin" + /obj/item/trash/tastybread name = "bread tube" icon_state = "tastybread" diff --git a/code/game/objects/items/trash_vr.dm b/code/game/objects/items/trash_vr.dm index a738179c7f..20f7e32c4c 100644 --- a/code/game/objects/items/trash_vr.dm +++ b/code/game/objects/items/trash_vr.dm @@ -26,7 +26,7 @@ return ..() -/obj/item/trash/liquidprotein - name = "\improper \"LiquidProtein\" ration" +/obj/item/trash/fancyplate + name = "dirty fancy plate" icon = 'icons/obj/trash_vr.dmi' - icon_state = "liquidprotein" \ No newline at end of file + icon_state = "fancyplate" diff --git a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm index a304184e25..6eb53a10f3 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm @@ -14,6 +14,10 @@ ..() network = using_map.station_networks +/obj/item/weapon/circuitboard/security/tv + name = T_BOARD("security camera monitor - television") + build_path = /obj/machinery/computer/security/wooden_tv + /obj/item/weapon/circuitboard/security/engineering name = T_BOARD("engineering camera monitor") build_path = /obj/machinery/computer/security/engineering diff --git a/code/game/objects/items/weapons/circuitboards/computer/computer.dm b/code/game/objects/items/weapons/circuitboards/computer/computer.dm index c15d73036a..2190650d67 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/computer.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/computer.dm @@ -21,6 +21,10 @@ name = T_BOARD("medical records console") build_path = /obj/machinery/computer/med_data +/obj/item/weapon/circuitboard/med_data/laptop + name = T_BOARD("medical records laptop") + build_path = /obj/machinery/computer/med_data/laptop + /obj/item/weapon/circuitboard/scan_consolenew name = T_BOARD("DNA machine") build_path = /obj/machinery/computer/scan_consolenew @@ -187,3 +191,8 @@ name = T_BOARD("RCON remote control console") build_path = /obj/machinery/computer/rcon origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3, TECH_POWER = 5) + +/obj/item/weapon/circuitboard/shutoff_monitor + name = T_BOARD("automatic shutoff valve monitor") + build_path = /obj/machinery/computer/shutoff_monitor + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4) \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm index 913506c9e6..6b59d13ea5 100644 --- a/code/game/objects/items/weapons/circuitboards/frame.dm +++ b/code/game/objects/items/weapons/circuitboards/frame.dm @@ -144,6 +144,14 @@ build_path = /obj/machinery/recharger/wallcharger board_type = new /datum/frame/frame_types/wall_charger +/obj/item/weapon/circuitboard/cell_charger + name = T_BOARD("heavy-duty cell charger") + build_path = /obj/machinery/cell_charger + board_type = new /datum/frame/frame_types/cell_charger + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 1, + /obj/item/stack/cable_coil = 5) + /obj/item/weapon/circuitboard/washing name = T_BOARD("washing machine") build_path = /obj/machinery/washing_machine @@ -162,6 +170,15 @@ /obj/item/weapon/stock_parts/gear = 1, /obj/item/weapon/reagent_containers/glass/beaker/large = 1) +/obj/item/weapon/circuitboard/distiller + build_path = /obj/machinery/portable_atmospherics/powered/reagent_distillery + board_type = new /datum/frame/frame_types/reagent_distillery + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 1, + /obj/item/weapon/stock_parts/micro_laser = 1, + /obj/item/weapon/stock_parts/motor = 2, + /obj/item/weapon/stock_parts/gear = 1) + /obj/item/weapon/circuitboard/teleporter_hub name = T_BOARD("teleporter hub") build_path = /obj/machinery/teleport/hub diff --git a/code/game/objects/items/weapons/circuitboards/machinery/research.dm b/code/game/objects/items/weapons/circuitboards/machinery/research.dm index 3fc0aa26ab..2bfdde4977 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/research.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/research.dm @@ -85,4 +85,12 @@ obj/item/weapon/circuitboard/rdserver/attackby(obj/item/I as obj, mob/user as mo /obj/item/weapon/stock_parts/matter_bin = 2, /obj/item/weapon/stock_parts/manipulator = 1, /obj/item/weapon/stock_parts/micro_laser = 1, - /obj/item/weapon/stock_parts/console_screen = 1) \ No newline at end of file + /obj/item/weapon/stock_parts/console_screen = 1) + +obj/item/weapon/circuitboard/ntnet_relay + name = "Circuit board (NTNet Quantum Relay)" + build_path = "/obj/machinery/ntnet_relay" + board_type = "machine" + origin_tech = list(TECH_DATA = 4) + req_components = list( + "/obj/item/stack/cable_coil" = 15) diff --git a/code/game/objects/items/weapons/circuitboards/mecha.dm b/code/game/objects/items/weapons/circuitboards/mecha.dm index fd351f8587..777146f385 100644 --- a/code/game/objects/items/weapons/circuitboards/mecha.dm +++ b/code/game/objects/items/weapons/circuitboards/mecha.dm @@ -32,6 +32,11 @@ icon_state = "mcontroller" origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 4) +/obj/item/weapon/circuitboard/mecha/gygax/medical + name = T_BOARD_MECHA("Serenity medical control") + icon_state = "mcontroller" + origin_tech = list(TECH_DATA = 4, TECH_BIO = 4) + /obj/item/weapon/circuitboard/mecha/gygax/main name = T_BOARD_MECHA("Gygax central control") icon_state = "mainboard" diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index 3d142b223a..4533f41537 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -87,4 +87,26 @@ text = "guy" if(FEMALE) text = "lady" - user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].") \ No newline at end of file + user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].") + +/obj/item/weapon/makeover + name = "makeover kit" + desc = "A tiny case containing a mirror and some contact lenses." + w_class = ITEMSIZE_TINY + icon = 'icons/obj/items.dmi' + icon_state = "trinketbox" + var/list/ui_users = list() + +/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob) + if(ishuman(user)) + to_chat(user, "You flip open \the [src] and begin to adjust your appearance.") + var/datum/nano_module/appearance_changer/AC = ui_users[user] + if(!AC) + AC = new(src, user) + AC.name = "SalonPro Porta-Makeover Deluxe™" + ui_users[user] = AC + AC.ui_interact(user) + var/mob/living/carbon/human/H = user + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if(istype(E)) + E.change_eye_color() \ No newline at end of file diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 86e3badc6f..d4a29d47f3 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -13,6 +13,10 @@ var/atom/target = null var/open_panel = 0 var/image_overlay = null + var/blast_dev = -1 + var/blast_heavy = -1 + var/blast_light = 2 + var/blast_flash = 3 /obj/item/weapon/plastique/New() wires = new(src) @@ -72,7 +76,7 @@ if(!target) target = src if(location) - explosion(location, -1, -1, 2, 3) + explosion(location, blast_dev, blast_heavy, blast_light, blast_flash) if(target) if (istype(target, /turf/simulated/wall)) @@ -88,3 +92,28 @@ /obj/item/weapon/plastique/attack(mob/M as mob, mob/user as mob, def_zone) return + +/obj/item/weapon/plastique/seismic + name = "seismic charge" + desc = "Used to dig holes in specific areas without too much extra hole." + + blast_heavy = 2 + blast_light = 4 + blast_flash = 7 + +/obj/item/weapon/plastique/seismic/attackby(var/obj/item/I, var/mob/user) + . = ..() + if(open_panel) + if(istype(I, /obj/item/weapon/stock_parts/micro_laser)) + var/obj/item/weapon/stock_parts/SP = I + var/new_blast_power = max(1, round(SP.rating / 2) + 1) + if(new_blast_power > blast_heavy) + to_chat(user, "You install \the [I] into \the [src].") + user.drop_from_inventory(I) + qdel(I) + blast_heavy = new_blast_power + blast_light = blast_heavy + round(new_blast_power * 0.5) + blast_flash = blast_light + round(new_blast_power * 0.75) + else + to_chat(user, "The [I] is not any better than the component already installed into this charge!") + return . \ No newline at end of file diff --git a/code/game/objects/items/weapons/explosives_vr.dm b/code/game/objects/items/weapons/explosives_vr.dm new file mode 100644 index 0000000000..a82ca02bbc --- /dev/null +++ b/code/game/objects/items/weapons/explosives_vr.dm @@ -0,0 +1,17 @@ +/obj/item/weapon/plastique/seismic/locked + desc = "Used to dig holes in specific areas without too much extra hole. Has extra mechanism that safely implodes the bomb if it is used in close proximity to the facility." + +/obj/item/weapon/plastique/seismic/locked/explode(var/location) + if(!target) + target = get_atom_on_turf(src) + if(!target) + target = src + + var/turf/T = get_turf(target) + if(T.z in using_map.map_levels) + target.visible_message("\The [src] lets out a loud beep as safeties trigger, before imploding and falling apart.") + target.overlays -= image_overlay + qdel(src) + return 0 + else + return ..() \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 7d725c36bb..625fb11e0c 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -41,6 +41,19 @@ /obj/item/weapon/grenade/spawnergrenade/manhacks/raider spawner_type = /mob/living/simple_mob/mechanical/viscerator/raider +/obj/item/weapon/grenade/spawnergrenade/manhacks/station + desc = "It is set to detonate in 5 seconds. It will deploy three weaponized survey drones." + deliveryamt = 3 + spawner_type = /mob/living/simple_mob/mechanical/viscerator/station + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ILLEGAL = 1) + +/obj/item/weapon/grenade/spawnergrenade/ward + name = "sentry delivery grenade" + desc = "It is set to detonate in 5 seconds. It will deploy a single thermal-optic sentry drone." + spawner_type = /mob/living/simple_mob/mechanical/ward/monitor/crew + deliveryamt = 1 + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_BLUESPACE = 2) + /obj/item/weapon/grenade/spawnergrenade/spesscarp name = "carp delivery grenade" spawner_type = /mob/living/simple_mob/animal/space/carp diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade_vr.dm b/code/game/objects/items/weapons/grenades/spawnergrenade_vr.dm new file mode 100644 index 0000000000..56204be984 --- /dev/null +++ b/code/game/objects/items/weapons/grenades/spawnergrenade_vr.dm @@ -0,0 +1,30 @@ +/obj/item/weapon/grenade/spawnergrenade/manhacks/station/locked + desc = "It is set to detonate in 5 seconds. It will deploy three weaponized survey drones. This one has a safety interlock that prevents release if used while in proximity to the facility." + req_access = list(access_armory) //for toggling safety + var/locked = 1 + +/obj/item/weapon/grenade/spawnergrenade/manhacks/station/locked/detonate() + if(locked) + var/turf/T = get_turf(src) + if(T.z in using_map.map_levels) + icon_state = initial(icon_state) + active = 0 + return 0 + return ..() + +/obj/item/weapon/grenade/spawnergrenade/manhacks/station/locked/attackby(obj/item/I, mob/user) + var/obj/item/weapon/card/id/id = I.GetID() + if(istype(id)) + if(check_access(id)) + locked = !locked + to_chat(user, "You [locked ? "enable" : "disable"] the safety lock on \the [src].") + else + to_chat(user, "Access denied.") + user.visible_message("[user] swipes \the [I] against \the [src].") + else + return ..() + +/obj/item/weapon/grenade/spawnergrenade/manhacks/station/locked/emag_act(var/remaining_charges,var/mob/user) + ..() + locked = !locked + to_chat(user, "You [locked ? "enable" : "disable"] the safety lock on \the [src]!") \ No newline at end of file diff --git a/code/game/objects/items/weapons/id cards/station_ids.dm b/code/game/objects/items/weapons/id cards/station_ids.dm index c157d42492..4d37f154b3 100644 --- a/code/game/objects/items/weapons/id cards/station_ids.dm +++ b/code/game/objects/items/weapons/id cards/station_ids.dm @@ -31,6 +31,7 @@ var/dorm = 0 // determines if this ID has claimed a dorm already var/mining_points = 0 // For redeeming at mining equipment vendors + var/survey_points = 0 // For redeeming at explorer equipment vendors. /obj/item/weapon/card/id/examine(mob/user) set src in oview(1) @@ -170,7 +171,7 @@ /obj/item/weapon/card/id/synthetic/Initialize() . = ..() - access = get_all_station_access() + access_synth + access = get_all_station_access().Copy() + access_synth /obj/item/weapon/card/id/centcom name = "\improper CentCom. ID" @@ -181,7 +182,7 @@ /obj/item/weapon/card/id/centcom/Initialize() . = ..() - access = get_all_centcom_access() + access = get_all_centcom_access().Copy() /obj/item/weapon/card/id/centcom/station/Initialize() . = ..() diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index 1804d576fd..c00d396e4d 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -13,6 +13,7 @@ var/implant_color = "b" var/allow_reagents = 0 var/malfunction = 0 + var/initialize_loc = BP_TORSO show_messages = 1 /obj/item/weapon/implant/proc/trigger(emote, source as mob) @@ -30,7 +31,7 @@ var/mob/living/carbon/human/H = source var/obj/item/organ/external/affected = H.get_organ(target_zone) if(affected) - affected.implants += src + affected.implants |= src part = affected if(part) forceMove(part) @@ -65,8 +66,9 @@ /obj/item/weapon/implant/proc/implant_loadout(var/mob/living/carbon/human/H) if(H) - var/obj/item/organ/external/affected = H.organs_by_name[BP_HEAD] + var/obj/item/organ/external/affected = H.organs_by_name[initialize_loc] if(handle_implant(H, affected)) + invisibility = initial(invisibility) post_implant(H) /obj/item/weapon/implant/Destroy() @@ -110,7 +112,7 @@ GLOBAL_LIST_BOILERPLATE(all_tracking_implants, /obj/item/weapon/implant/tracking ..() /obj/item/weapon/implant/tracking/post_implant(var/mob/source) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) /obj/item/weapon/implant/tracking/Destroy() STOP_PROCESSING(SSobj, src) @@ -563,7 +565,7 @@ the implant may become unstable and either pre-maturely inject the subject or si /obj/item/weapon/implant/death_alarm/post_implant(mob/source as mob) mobname = source.real_name - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) ////////////////////////////// // Compressed Matter Implant diff --git a/code/game/objects/items/weapons/implants/implant_vr.dm b/code/game/objects/items/weapons/implants/implant_vr.dm index bec23b4c2b..17d1d6f6d2 100644 --- a/code/game/objects/items/weapons/implants/implant_vr.dm +++ b/code/game/objects/items/weapons/implants/implant_vr.dm @@ -33,6 +33,7 @@ source.add_language(LANGUAGE_BIRDSONG) source.add_language(LANGUAGE_SAGARU) source.add_language(LANGUAGE_CANILUNZT) + source.add_language(LANGUAGE_SLAVIC) source.add_language(LANGUAGE_SOL_COMMON) //In case they're giving a xenomorph an implant or something. /obj/item/weapon/implant/vrlanguage/post_implant(mob/source) diff --git a/code/game/objects/items/weapons/implants/implantaugment.dm b/code/game/objects/items/weapons/implants/implantaugment.dm new file mode 100644 index 0000000000..10873d1d06 --- /dev/null +++ b/code/game/objects/items/weapons/implants/implantaugment.dm @@ -0,0 +1,196 @@ +////////////////////////////// +// Nanite Organ Implant +////////////////////////////// +/obj/item/weapon/implant/organ + name = "nanite fabrication implant" + desc = "A buzzing implant covered in a writhing layer of metal insects." + icon_state = "implant_evil" + origin_tech = list(TECH_MATERIAL = 5, TECH_BIO = 2, TECH_ILLEGAL = 2) + + var/organ_to_implant = /obj/item/organ/internal/augment/bioaugment/thermalshades + var/organ_display_name = "unknown organ" + +/obj/item/weapon/implant/organ/get_data() + var/dat = {" +Implant Specifications:
    +Name: \"GreyDoctor\" Class Nanite Hive
    +Life: Activates upon implantation, destroying itself in the process.
    +Important Notes: Nanites will fail to complete their task if a suitable location cannot be found for the organ.
    +
    +Implant Details:
    +Function: Nanites will fabricate: [organ_display_name]
    +Special Features: Organ identification protocols.
    +Integrity: N/A"} + return dat + +/obj/item/weapon/implant/organ/post_implant(var/mob/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + + var/obj/item/organ/NewOrgan = new organ_to_implant() + + var/obj/item/organ/external/E = H.get_organ(NewOrgan.parent_organ) + to_chat(H, "You feel a tingling sensation in your [part].") + if(E && !(H.internal_organs_by_name[NewOrgan.organ_tag])) + spawn(rand(1 SECONDS, 30 SECONDS)) + to_chat(H, "You feel a pressure in your [E] as the tingling fades, the lump caused by the implant now gone.") + + NewOrgan.forceMove(H) + NewOrgan.owner = H + if(E.internal_organs == null) + E.internal_organs = list() + E.internal_organs |= NewOrgan + H.internal_organs_by_name[NewOrgan.organ_tag] = NewOrgan + H.internal_organs |= NewOrgan + NewOrgan.handle_organ_mod_special() + + spawn(1) + if(!QDELETED(src)) + qdel(src) + + else + qdel(NewOrgan) + to_chat(H, "You feel a pinching sensation in your [part]. The implant remains.") + +/obj/item/weapon/implant/organ/islegal() + return 0 + +/* + * Arm / leg mounted augments. + */ + +/obj/item/weapon/implant/organ/limbaugment + name = "nanite implant" + + organ_to_implant = /obj/item/organ/internal/augment/armmounted/taser + organ_display_name = "physiological augment" + + var/list/possible_targets = list(O_AUG_L_FOREARM, O_AUG_R_FOREARM) + +/obj/item/weapon/implant/organ/limbaugment/post_implant(var/mob/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + + var/obj/item/organ/NewOrgan = new organ_to_implant() + + var/obj/item/organ/external/E = setup_augment_slots(H, NewOrgan) + to_chat(H, "You feel a tingling sensation in your [part].") + if(E && istype(E) && !(H.internal_organs_by_name[NewOrgan.organ_tag])) + spawn(rand(1 SECONDS, 30 SECONDS)) + to_chat(H, "You feel a pressure in your [E] as the tingling fades, the lump caused by the implant now gone.") + + NewOrgan.forceMove(H) + NewOrgan.owner = H + if(E.internal_organs == null) + E.internal_organs = list() + E.internal_organs |= NewOrgan + H.internal_organs_by_name[NewOrgan.organ_tag] = NewOrgan + H.internal_organs |= NewOrgan + NewOrgan.handle_organ_mod_special() + + spawn(1) + if(!QDELETED(src)) + qdel(src) + + else + qdel(NewOrgan) + to_chat(H, "You feel a pinching sensation in your [part]. The implant remains.") + +/obj/item/weapon/implant/organ/limbaugment/proc/setup_augment_slots(var/mob/living/carbon/human/H, var/obj/item/organ/internal/augment/armmounted/I) + var/list/Choices = possible_targets.Copy() + + for(var/targ in possible_targets) + if(H.internal_organs_by_name[targ]) + Choices -= targ + + var/target_choice = null + if(Choices && Choices.len) + if(Choices.len == 1) + target_choice = Choices[1] + else + target_choice = input("Choose augment location:") in Choices + + else + return FALSE + + if(target_choice) + switch(target_choice) + if(O_AUG_R_HAND) + I.organ_tag = O_AUG_R_HAND + I.parent_organ = BP_R_HAND + I.target_slot = slot_r_hand + if(O_AUG_L_HAND) + I.organ_tag = O_AUG_L_HAND + I.parent_organ = BP_L_HAND + I.target_slot = slot_l_hand + + if(O_AUG_R_FOREARM) + I.organ_tag = O_AUG_R_FOREARM + I.parent_organ = BP_R_ARM + I.target_slot = slot_r_hand + if(O_AUG_L_FOREARM) + I.organ_tag = O_AUG_L_FOREARM + I.parent_organ = BP_L_ARM + I.target_slot = slot_l_hand + + if(O_AUG_R_UPPERARM) + I.organ_tag = O_AUG_R_UPPERARM + I.parent_organ = BP_R_ARM + I.target_slot = slot_r_hand + if(O_AUG_L_UPPERARM) + I.organ_tag = O_AUG_L_UPPERARM + I.parent_organ = BP_L_ARM + I.target_slot = slot_l_hand + + . = H.get_organ(I.parent_organ) + +/* + * Limb implant primary subtypes. + */ + +/obj/item/weapon/implant/organ/limbaugment/upperarm + organ_to_implant = /obj/item/organ/internal/augment/armmounted/shoulder/multiple + organ_display_name = "multi-use augment" + + possible_targets = list(O_AUG_R_UPPERARM,O_AUG_L_UPPERARM) + +/obj/item/weapon/implant/organ/limbaugment/wrist + organ_to_implant = /obj/item/organ/internal/augment/armmounted/hand + organ_display_name = "wrist augment" + + possible_targets = list(O_AUG_R_HAND,O_AUG_L_HAND) + +/* + * Limb implant general subtypes. + */ + +// Wrist +/obj/item/weapon/implant/organ/limbaugment/wrist/sword + organ_to_implant = /obj/item/organ/internal/augment/armmounted/hand/sword + organ_display_name = "weapon augment" + +// Fore-arm +/obj/item/weapon/implant/organ/limbaugment/laser + organ_to_implant = /obj/item/organ/internal/augment/armmounted + organ_display_name = "weapon augment" + +/obj/item/weapon/implant/organ/limbaugment/dart + organ_to_implant = /obj/item/organ/internal/augment/armmounted/dartbow + organ_display_name = "weapon augment" + +// Upper-arm. +/obj/item/weapon/implant/organ/limbaugment/upperarm/medkit + organ_to_implant = /obj/item/organ/internal/augment/armmounted/shoulder/multiple/medical + +/obj/item/weapon/implant/organ/limbaugment/upperarm/surge + organ_to_implant = /obj/item/organ/internal/augment/armmounted/shoulder/surge + +/* + * Others + */ + +/obj/item/weapon/implant/organ/pelvic + name = "nanite fabrication implant" + + organ_to_implant = /obj/item/organ/internal/augment/bioaugment/sprint_enhance + organ_display_name = "pelvic augment" diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index ae00895361..93fef6ca92 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -179,3 +179,103 @@ src.imp = new /obj/item/weapon/implant/language/eal( src ) ..() return + +/obj/item/weapon/implantcase/shades + name = "glass case - 'Integrated Shades'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/shades/New() + src.imp = new /obj/item/weapon/implant/organ( src ) + ..() + return + +/obj/item/weapon/implantcase/taser + name = "glass case - 'Taser'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/taser/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment( src ) + ..() + return + +/obj/item/weapon/implantcase/laser + name = "glass case - 'Laser'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/laser/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/laser( src ) + ..() + return + +/obj/item/weapon/implantcase/dart + name = "glass case - 'Dart'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/dart/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/dart( src ) + ..() + return + +/obj/item/weapon/implantcase/toolkit + name = "glass case - 'Toolkit'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/toolkit/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm( src ) + ..() + return + +/obj/item/weapon/implantcase/medkit + name = "glass case - 'Toolkit'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/medkit/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/medkit( src ) + ..() + return + +/obj/item/weapon/implantcase/surge + name = "glass case - 'Muscle Overclocker'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/surge/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/surge( src ) + ..() + return + +/obj/item/weapon/implantcase/analyzer + name = "glass case - 'Scanner'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/analyzer/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist( src ) + ..() + return + +/obj/item/weapon/implantcase/sword + name = "glass case - 'Scanner'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/sword/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/sword( src ) + ..() + return + +/obj/item/weapon/implantcase/sprinter + name = "glass case - 'Sprinter'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/sprinter/New() + src.imp = new /obj/item/weapon/implant/organ/pelvic( src ) + ..() + return diff --git a/code/game/objects/items/weapons/implants/implantdud.dm b/code/game/objects/items/weapons/implants/implantdud.dm new file mode 100644 index 0000000000..896ea2358a --- /dev/null +++ b/code/game/objects/items/weapons/implants/implantdud.dm @@ -0,0 +1,30 @@ +/obj/item/weapon/implant/dud + name = "unknown implant" + desc = "A small device with small connector wires." + icon = 'icons/obj/device.dmi' + icon_state = "implant" + initialize_loc = BP_HEAD + var/roundstart = TRUE + +/obj/item/weapon/implant/dud/torso + name = "unknown implant" + desc = "A small device with small connector wires." + icon = 'icons/obj/device.dmi' + icon_state = "implant" + initialize_loc = BP_TORSO + +/obj/item/weapon/implant/dud/old + name = "old implant" + desc = "A small device with small connector wires." + icon = 'icons/obj/device.dmi' + icon_state = "implant" + roundstart = FALSE + +/obj/item/weapon/implant/dud/Initialize() + ..() + if(roundstart) + invisibility = 100 + ..() + spawn(3) + if(!ishuman(loc) && !QDELETED(src)) + qdel(src) diff --git a/code/game/objects/items/weapons/implants/neuralbasic.dm b/code/game/objects/items/weapons/implants/neuralbasic.dm index d744c80fde..0c5fe2b3da 100644 --- a/code/game/objects/items/weapons/implants/neuralbasic.dm +++ b/code/game/objects/items/weapons/implants/neuralbasic.dm @@ -1,6 +1,7 @@ /obj/item/weapon/implant/neural name = "neural framework implant" desc = "A small metal casing with numerous wires stemming off of it." + initialize_loc = BP_HEAD var/obj/item/organ/internal/brain/my_brain = null var/target_state = null var/robotic_brain = FALSE @@ -104,3 +105,10 @@ Implant Specifics:
    "} my_brain.take_damage(15) my_brain = null return + +/obj/item/weapon/implant/neural/roundstart/Initialize() + invisibility = 100 + ..() + spawn(3) + if(!ishuman(loc) && !QDELETED(src)) + qdel(src) diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 0db669545c..4911846d65 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -98,11 +98,11 @@

    OPERATING PRINCIPLES


    -
  • The supermatter crystal serves as the fundamental power source of the engine. Upon being charged, it begins to emit large amounts of heat and radiation, as well and oxygen and phoron gas. As oxygen accelerates the reaction, and phoron carries the risk of fire, these must be filtered out. NOTE: Supermatter radiation will not charge radiation collectors.
  • +
  • The supermatter crystal serves as the fundamental power source of the engine. Upon being charged, it begins to emit large amounts of heat and radiation, as well and oxygen and phoron gas. As oxygen accelerates the reaction and reacts with phoron to start a fire, it must be filtered out. It's recommended to filter out all gases besides nitrogen for standard operation.

  • -
  • Air in the reactor chamber housing the supermatter is circulated through the reactor loop, which passes through the filters and thermoelectric generators. The thermoelectric generators transfer heat from the reactor loop to the colder radiator loop, thereby generating power. Additional power is generated from internal turbines in the circulators.
  • +
  • Gas in the reactor chamber housing the supermatter is circulated through the reactor loop, which passes through the filters and thermoelectric generators. The thermoelectric generators transfer heat from the reactor loop to the colder radiator loop, thereby generating power. Additional power is generated from internal turbines in the circulators.

  • -
  • Air in the radiator loop is circulated through the radiator bank, located in space. This rapidly cools the air, preserving the temperature differential needed for power generation.
  • +
  • Gas in the radiator loop is circulated through the radiator bank, located in space. This rapidly cools the air, preserving the temperature differential needed for power generation.

  • The MK 1 Prototype Thermoelectric Supermatter Engine is designed to operate at reactor temperatures of 3000K to 4000K and generate up to 1MW of power. Beyond 1MW, the thermoelectric generators will begin to lose power through electrical discharge, reducing efficiency, but additional power generation remains feasible.

  • @@ -113,19 +113,25 @@
  • Do not allow supermatter to contact any solid object apart from specially-designed supporting pallet.
  • Do not directly view supermatter without meson goggles.
  • While handles on pallet allow moving the supermatter via pulling, pushing should not be attempted.
  • +
  • Note that prosthetics do not protect against radiation or viewing the supermatter.

  • -

    STARTUP PROCEDURE

    +

    STANDARD STARTUP PROCEDURE

      -
    1. Fill reactor loop and radiator loop with two (2) standard canisters of nitrogen gas each.
    2. -
    3. Ensure that pumps and filters are on and operating at maximum power.
    4. -
    5. Fire 8-9 pulses from emitter at supermatter crystal. Reactor blast doors must be open for this procedure.
    6. +
    7. Fill reactor loop and radiator loop with three (3) standard canisters of nitrogen gas each.
    8. +
    9. Fill the waste handling radiator loop with one (1) standard canister of carbon dioxide gas.
    10. +
    11. Enable both the high power gas pumps near the thermo-electric generators and maximize the desired output.
    12. +
    13. Enable both the omni-filters and ensure they are set to filter nitrogen back into the system.
    14. +
    15. Enable the gas pump from the filters to waste handling and maximize the desired output.
    16. +
    17. Close the monitoring room blast doors and open the reactor blast doors,
    18. +
    19. Fire 8-9 pulses from emitter at supermatter crystal. The expected power output is around a megawatt. NOTE: It will take a few minutes to heat up.
    20. +
    21. Close the reactor blast doors and keep the monitoring room blast doors closed to prevent radiation leaking.

    OPERATION AND MAINTENANCE

    1. Ensure that radiation protection and meson goggles are worn at all times while working in the engine room.
    2. Ensure that reactor and radiator loops are undamaged and unobstructed.
    3. -
    4. Ensure that phoron and oxygen gas exhaust from filters is properly contained or disposed. Do not allow exhaust pressure to exceed 4500 kPa.
    5. +
    6. Ensure that, in a standard setup, only nitrogen is being filtered back into the system. Do not allow exhaust pressure to exceed 4500 kPa.
    7. Ensure that engine room Area Power Controller (APC) and engine Superconducting Magnetic Energy Storage unit (SMES) are properly charged.
    8. Ensure that reactor temperature does not exceed 5000K. In event of reactor temperature exceeding 5000K, see EMERGENCY COOLING PROCEDURE.
    9. In event of imminent and/or unavoidable delamination, see EJECTION PROCEDURE.
    10. @@ -135,12 +141,14 @@
      1. Open Emergency Cooling Valve 1 and Emergency Cooling Valve 2.
      2. When reactor temperature returns to safe operating levels, close Emergency Cooling Valve 1 and Emergency Cooling Valve 2.
      3. +
      4. Adding additional gas to the loops can have a positive effect in reducing reactor temperature.
      5. If reactor temperature does not return to safe operating levels, see EJECTION PROCEDURE.

      EJECTION PROCEDURE

        -
      1. Press Engine Ventilatory Control button to open engine core vent to space.
      2. +
      3. Ensure the engine room has power. The blast doors and ejection platform are unresponsive without power.
      4. +
      5. Press Engine Ventilatory Control button to open engine core blast door to space.
      6. Press Emergency Core Eject button to eject supermatter crystal. NOTE: Attempting crystal ejection while engine core vent is closed will result in ejection failure.
      7. In event of ejection failure, pending
      diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index 4f08c61a4c..a911d1e137 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -42,6 +42,9 @@ if(!isnull(matter[material_type])) matter[material_type] *= force_divisor // May require a new var instead. + if(!(material.conductive)) + src.flags |= NOCONDUCT + /obj/item/weapon/material/get_material() return material @@ -67,7 +70,7 @@ if(applies_material_colour) color = material.icon_colour if(material.products_need_process()) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) update_force() /obj/item/weapon/material/Destroy() diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index 529e846621..8df81e434c 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -61,6 +61,51 @@ return return ..() +/obj/item/weapon/material/shard/afterattack(var/atom/target, mob/living/carbon/human/user as mob) + var/active_hand //hand the shard is in + var/will_break = FALSE + var/protected_hands = FALSE //this is a fucking mess + var/break_damage = 4 + var/light_glove_d = rand(2, 4) + var/no_glove_d = rand(4, 6) + var/list/forbidden_gloves = list( + /obj/item/clothing/gloves/sterile, + /obj/item/clothing/gloves/knuckledusters + ) + + if(src == user.l_hand) + active_hand = BP_L_HAND + else if(src == user.r_hand) + active_hand = BP_R_HAND + else + return // If it's not actually in our hands anymore, we were probably gentle with it + + active_hand = (src == user.l_hand) ? BP_L_HAND : BP_R_HAND // May not actually be faster than an if-else block, but a little bit cleaner -Ater + + if(prob(75)) + will_break = TRUE + + if(user.gloves && (user.gloves.body_parts_covered & HANDS) && istype(user.gloves, /obj/item/clothing/gloves)) // Not-gloves aren't gloves, and therefore don't protect us + protected_hands = TRUE // If we're wearing gloves we can probably handle it just fine + for(var/I in forbidden_gloves) + if(istype(user.gloves, I)) // forbidden_gloves is a blacklist, so if we match anything in there, our hands are not protected + protected_hands = FALSE + break + + if(user.gloves && !protected_hands) + to_chat(user, "\The [src] partially cuts into your hand through your gloves as you hit \the [target]!") + user.apply_damage(light_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) // Ternary to include break damage + + else if(!user.gloves) + to_chat(user, "\The [src] cuts into your hand as you hit \the [target]!") + user.apply_damage(no_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) + + if(will_break && src.loc == user) // If it's not in our hand anymore + user.visible_message("[user] hit \the [target] with \the [src], shattering it!", "You shatter \the [src] in your hand!") + playsound(user, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 30, 1) + qdel(src) + return + /obj/item/weapon/material/shard/Crossed(AM as mob|obj) ..() //VOREStation Edit begin: SHADEKIN diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index 9efe911360..46551e588a 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -11,8 +11,7 @@ hitsound = 'sound/weapons/bladeslice.ogg' /obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - - if(default_parry_check(user, attacker, damage_source) && prob(50)) + if(unique_parry_check(user, attacker, damage_source) && prob(50)) user.visible_message("\The [user] parries [attack_text] with \the [src]!") playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) return 1 diff --git a/code/game/objects/items/weapons/melee/deflect.dm b/code/game/objects/items/weapons/melee/deflect.dm new file mode 100644 index 0000000000..293d2b9e29 --- /dev/null +++ b/code/game/objects/items/weapons/melee/deflect.dm @@ -0,0 +1,31 @@ +/* + * The home of basic deflect / defense code. + */ + +/obj/item/weapon/melee + var/defend_chance = 5 // The base chance for the weapon to parry. + var/projectile_parry_chance = 0 // The base chance for a projectile to be deflected. + +/obj/item/weapon/melee/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(.) + return . + if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + return 1 + if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + return 1 + + return 0 + +/obj/item/weapon/melee/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + if(.) + return . + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 72fe26da36..d935d4eaa2 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -12,6 +12,12 @@ var/lpower = 2 var/lcolor = "#0099FF" + // If it uses energy. + var/use_cell = FALSE + var/hitcost = 120 + var/obj/item/weapon/cell/bcell = null + var/cell_type = /obj/item/weapon/cell/device + /obj/item/weapon/melee/energy/proc/activate(mob/living/user) if(active) return @@ -38,7 +44,31 @@ w_class = initial(w_class) set_light(0,0) +/obj/item/weapon/melee/energy/proc/use_charge(var/cost) + if(active) + if(bcell) + if(bcell.checked_use(cost)) + return 1 + else + return 0 + return null + +/obj/item/weapon/melee/energy/examine(mob/user) + if(!..(user, 1)) + return + + if(use_cell) + if(bcell) + to_chat(user, "The blade is [round(bcell.percent())]% charged.") + if(!bcell) + to_chat(user, "The blade does not have a power source installed.") + /obj/item/weapon/melee/energy/attack_self(mob/living/user as mob) + if(use_cell) + if((!bcell || bcell.charge < hitcost) && !active) + to_chat(user, "\The [src] does not seem to have power.") + return + var/datum/gender/TU = gender_datums[user.get_visible_gender()] if (active) if ((CLUMSY in user.mutations) && prob(50)) @@ -64,6 +94,37 @@ "\The [user] is falling on \the [src]! It looks like [TU.he] [TU.is] trying to commit suicide.")) return (BRUTELOSS|FIRELOSS) +/obj/item/weapon/melee/energy/attack(mob/M, mob/user) + if(active && use_cell) + if(!use_charge(hitcost)) + deactivate(user) + visible_message("\The [src]'s blade flickers, before deactivating.") + return ..() + +/obj/item/weapon/melee/energy/attackby(obj/item/weapon/W, mob/user) + if(use_cell) + if(istype(W, cell_type)) + if(!bcell) + user.drop_item() + W.loc = src + bcell = W + to_chat(user, "You install a cell in [src].") + update_icon() + else + to_chat(user, "[src] already has a cell.") + else if(W.is_screwdriver() && bcell) + bcell.update_icon() + bcell.forceMove(get_turf(loc)) + bcell = null + to_chat(user, "You remove the cell from \the [src].") + deactivate() + update_icon() + return + return ..() + +/obj/item/weapon/melee/energy/get_cell() + return bcell + /* * Energy Axe */ @@ -90,11 +151,13 @@ /obj/item/weapon/melee/energy/axe/activate(mob/living/user) ..() + damtype = SEARING icon_state = "axe1" to_chat(user, "\The [src] is now energised.") /obj/item/weapon/melee/energy/axe/deactivate(mob/living/user) ..() + damtype = BRUTE icon_state = initial(icon_state) to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") @@ -103,6 +166,20 @@ visible_message("\The [user] swings \the [src] towards [TU.his] head! It looks like [TU.he] [TU.is] trying to commit suicide.") return (BRUTELOSS|FIRELOSS) +/obj/item/weapon/melee/energy/axe/charge + name = "charge axe" + desc = "An energised axe." + active_force = 35 + active_throwforce = 20 + force = 15 + + use_cell = TRUE + hitcost = 120 + +/obj/item/weapon/melee/energy/axe/charge/loaded/New() + ..() + bcell = new/obj/item/weapon/cell/device/weapon(src) + /* * Energy Sword */ @@ -127,6 +204,8 @@ var/random_color = TRUE var/active_state = "sword" + projectile_parry_chance = 65 + /obj/item/weapon/melee/energy/sword/dropped(var/mob/user) ..() if(!istype(loc,/mob)) @@ -173,7 +252,7 @@ icon_state = initial(icon_state) /obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) + if(active && default_parry_check(user, attacker, damage_source) && prob(60)) user.visible_message("\The [user] parries [attack_text] with \the [src]!") var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() @@ -181,8 +260,27 @@ spark_system.start() playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) return 1 + if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + return 0 +/obj/item/weapon/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + /obj/item/weapon/melee/energy/sword/pirate name = "energy cutlass" desc = "Arrrr matey." @@ -215,6 +313,7 @@ lpower = 2 lcolor = "#0000FF" active_state = "ionic_rapier" + projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. /obj/item/weapon/melee/energy/sword/ionic_rapier/afterattack(var/atom/movable/AM, var/mob/living/user, var/proximity) if(istype(AM, /obj) && proximity && active) @@ -250,65 +349,9 @@ origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) active_force = 25 armor_penetration = 25 + projectile_parry_chance = 40 - var/hitcost = 75 - var/obj/item/weapon/cell/bcell = null - var/cell_type = /obj/item/weapon/cell/device - -/obj/item/weapon/melee/energy/sword/charge/proc/use_charge(var/cost) - if(active) - if(bcell) - if(bcell.checked_use(cost)) - return 1 - else - return 0 - return null - -/obj/item/weapon/melee/energy/sword/charge/examine(mob/user) - if(!..(user, 1)) - return - - if(bcell) - to_chat(user, "The blade is [round(bcell.percent())]% charged.") - if(!bcell) - to_chat(user, "The blade does not have a power source installed.") - -/obj/item/weapon/melee/energy/sword/charge/attack_self(mob/user as mob) - if((!bcell || bcell.charge < hitcost) && !active) - to_chat(user, "\The [src] does not seem to have power.") - return - ..() - -/obj/item/weapon/melee/energy/sword/charge/attack(mob/M, mob/user) - if(active) - if(!use_charge(hitcost)) - deactivate(user) - visible_message("\The [src]'s blade flickers, before retracting.") - return ..() - -/obj/item/weapon/melee/energy/sword/charge/attackby(obj/item/weapon/W, mob/user) - if(istype(W, cell_type)) - if(!bcell) - user.drop_item() - W.loc = src - bcell = W - to_chat(user, "You install a cell in [src].") - update_icon() - else - to_chat(user, "[src] already has a cell.") - else if(W.is_screwdriver() && bcell) - bcell.update_icon() - bcell.forceMove(get_turf(loc)) - bcell = null - to_chat(user, "You remove the cell from \the [src].") - deactivate() - update_icon() - return - else - ..() - -/obj/item/weapon/melee/energy/sword/charge/get_cell() - return bcell + hitcost = 75 /obj/item/weapon/melee/energy/sword/charge/loaded/New() ..() @@ -336,6 +379,7 @@ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") var/mob/living/creator var/datum/effect/effect/system/spark_spread/spark_system + projectile_parry_chance = 60 lcolor = "#00FF00" /obj/item/weapon/melee/energy/blade/New() @@ -373,6 +417,37 @@ host.drop_from_inventory(src) spawn(1) if(src) qdel(src) +/obj/item/weapon/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(default_parry_check(user, attacker, damage_source) && prob(60)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + + return 0 + +/obj/item/weapon/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + /* *Energy Spear */ diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 0cfdd4792d..f8669aef9d 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -10,6 +10,7 @@ GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) throw_speed = 5 throw_range = 10 w_class = ITEMSIZE_NORMAL + flags = NOCONDUCT attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") var/mopping = 0 var/mopcount = 0 diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index a1a2756eca..5ea586f43d 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -29,6 +29,9 @@ return 1 +/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. + return default_parry_check(user, attacker, damage_source) + /obj/item/weapon/shield name = "shield" var/base_block_chance = 50 diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 18c60af449..f5ea4ead6f 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -422,3 +422,10 @@ H.visible_message("\The [src] decides not to unpack \the [src]!", \ "You decide not to unpack \the [src]!") return + +/obj/item/weapon/storage/backpack/satchel/ranger + name = "ranger satchel" + desc = "A satchel designed for the Go Go ERT Rangers series to allow for slightly bigger carry capacity for the ERT-Rangers.\ + Unlike the show claims, it is not a phoron-enhanced satchel of holding with plot-relevant content." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_satchel" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/backpack_vr.dm b/code/game/objects/items/weapons/storage/backpack_vr.dm index d836d23581..5e121a2b35 100644 --- a/code/game/objects/items/weapons/storage/backpack_vr.dm +++ b/code/game/objects/items/weapons/storage/backpack_vr.dm @@ -128,4 +128,9 @@ /obj/item/weapon/storage/backpack/dufflebag/fluff //Black dufflebag without syndie buffs. name = "plain black dufflebag" desc = "A large dufflebag for holding extra tactical supplies." - icon_state = "duffle_syndie" \ No newline at end of file + icon_state = "duffle_syndie" + +/obj/item/weapon/storage/backpack + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/back.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/back.dmi') diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 66cdac3b63..ddb8c489f6 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -177,7 +177,8 @@ /obj/item/weapon/gun/projectile/sec, /obj/item/weapon/gun/projectile/p92x, /obj/item/taperoll, - /obj/item/weapon/gun/projectile/colt/detective + /obj/item/weapon/gun/projectile/colt/detective, + /obj/item/device/holowarrant ) /obj/item/weapon/storage/belt/detective @@ -219,7 +220,8 @@ /obj/item/weapon/flame/lighter, /obj/item/weapon/reagent_containers/food/snacks/donut/, /obj/item/ammo_magazine, - /obj/item/weapon/gun/projectile/colt/detective + /obj/item/weapon/gun/projectile/colt/detective, + /obj/item/device/holowarrant ) /obj/item/weapon/storage/belt/soulstone @@ -433,3 +435,8 @@ icon_state = "fannypack_yellow" item_state = "fannypack_yellow" +/obj/item/weapon/storage/belt/ranger + name = "ranger belt" + desc = "The fancy utility-belt holding the tools, cuffs and gadgets of the Go Go ERT-Rangers. The belt buckle is not real phoron, but it is still surprisingly comfortable to wear." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_belt" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/belt_vr.dm b/code/game/objects/items/weapons/storage/belt_vr.dm new file mode 100644 index 0000000000..2e0829c153 --- /dev/null +++ b/code/game/objects/items/weapons/storage/belt_vr.dm @@ -0,0 +1,4 @@ +/obj/item/weapon/storage/belt + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/belt.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/belt.dmi') \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/boxes_vr.dm b/code/game/objects/items/weapons/storage/boxes_vr.dm index b1690898e8..f2cbd06aed 100644 --- a/code/game/objects/items/weapons/storage/boxes_vr.dm +++ b/code/game/objects/items/weapons/storage/boxes_vr.dm @@ -36,3 +36,16 @@ name = "box of command keys" desc = "A box full of service keys, for the HoP to give out as necessary." starts_with = list(/obj/item/device/encryptionkey/headset_service = 7) + +/obj/item/weapon/storage/box/survival/space + name = "boxed emergency suit and helmet" + icon_state = "survivaleng" + starts_with = list( + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/head/helmet/space/emergency, + /obj/item/clothing/mask/breath, + /obj/item/weapon/tank/emergency/oxygen/double + ) + +/obj/item/weapon/storage/secure/briefcase/trashmoney + starts_with = list(/obj/item/weapon/spacecash/c200 = 10) diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm index 7d754d8f14..1d1934bf8a 100644 --- a/code/game/objects/items/weapons/storage/fancy.dm +++ b/code/game/objects/items/weapons/storage/fancy.dm @@ -67,6 +67,7 @@ item_state = "candlebox5" throwforce = 2 slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_SMALL * 5 starts_with = list(/obj/item/weapon/flame/candle = 5) /obj/item/weapon/storage/fancy/whitecandle_box @@ -78,6 +79,7 @@ item_state = "whitecandlebox5" throwforce = 2 slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_SMALL * 5 starts_with = list(/obj/item/weapon/flame/candle/white = 5) /obj/item/weapon/storage/fancy/blackcandle_box @@ -89,6 +91,7 @@ item_state = "blackcandlebox5" throwforce = 2 slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_SMALL * 5 starts_with = list(/obj/item/weapon/flame/candle/black = 5) @@ -179,6 +182,21 @@ return ..() +/* + * Cracker Packet + */ + +/obj/item/weapon/storage/fancy/crackers + name = "\improper Getmore Crackers" + icon = 'icons/obj/food.dmi' + icon_state = "crackerbox" + icon_type = "cracker" + max_storage_space = ITEMSIZE_COST_TINY * 6 + max_w_class = ITEMSIZE_TINY + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cracker) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cracker = 6) + //////////// //CIG PACK// //////////// diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index d436af1936..2fd85ec7c2 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -156,10 +156,15 @@ /obj/item/weapon/storage/firstaid/clotting name = "clotting kit" desc = "Contains chemicals to stop bleeding." - icon_state = "clottingkit" // VOREStation edit max_storage_space = ITEMSIZE_COST_SMALL * 7 starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting = 8) +/obj/item/weapon/storage/firstaid/bonemed + name = "bone repair kit" + desc = "Contains chemicals to mend broken bones." + max_storage_space = ITEMSIZE_COST_SMALL * 7 + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed = 8) + /* * Pill Bottles */ @@ -177,15 +182,25 @@ use_sound = null max_storage_space = ITEMSIZE_COST_TINY * 14 max_w_class = ITEMSIZE_TINY + var/wrapper_color + var/label var/label_text = "" var/base_name = " " var/base_desc = " " -/obj/item/weapon/storage/pill_bottle/New() - ..() +/obj/item/weapon/storage/pill_bottle/Initialize() + . = ..() base_name = name base_desc = desc + update_icon() + +/obj/item/weapon/storage/pill_bottle/update_icon() + overlays.Cut() + if(wrapper_color) + var/image/I = image(icon, "pillbottle_wrap") + I.color = wrapper_color + overlays += I /obj/item/weapon/storage/pill_bottle/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) @@ -216,61 +231,71 @@ desc = "[base_desc] It is labeled \"[label_text]\"." /obj/item/weapon/storage/pill_bottle/antitox - name = "bottle of Dylovene pills" + name = "pill bottle (Dylovene)" desc = "Contains pills used to counter toxins." starts_with = list(/obj/item/weapon/reagent_containers/pill/antitox = 7) + wrapper_color = COLOR_GREEN /obj/item/weapon/storage/pill_bottle/bicaridine - name = "bottle of Bicaridine pills" + name = "pill bottle (Bicaridine)" desc = "Contains pills used to stabilize the severely injured." starts_with = list(/obj/item/weapon/reagent_containers/pill/bicaridine = 7) + wrapper_color = COLOR_MAROON /obj/item/weapon/storage/pill_bottle/dexalin_plus - name = "bottle of Dexalin Plus pills" + name = "pill bottle (Dexalin Plus)" desc = "Contains pills used to treat extreme cases of oxygen deprivation." starts_with = list(/obj/item/weapon/reagent_containers/pill/dexalin_plus = 7) + wrapper_color = "#3366cc" /obj/item/weapon/storage/pill_bottle/dermaline - name = "bottle of Dermaline pills" + name = "pill bottle (Dermaline)" desc = "Contains pills used to treat burn wounds." starts_with = list(/obj/item/weapon/reagent_containers/pill/dermaline = 7) + wrapper_color = "#e8d131" /obj/item/weapon/storage/pill_bottle/dylovene - name = "bottle of Dylovene pills" + name = "pill bottle (Dylovene)" desc = "Contains pills used to treat toxic substances in the blood." starts_with = list(/obj/item/weapon/reagent_containers/pill/dylovene = 7) + wrapper_color = COLOR_GREEN /obj/item/weapon/storage/pill_bottle/inaprovaline - name = "bottle of Inaprovaline pills" + name = "pill bottle (Inaprovaline)" desc = "Contains pills used to stabilize patients." starts_with = list(/obj/item/weapon/reagent_containers/pill/inaprovaline = 7) + wrapper_color = COLOR_PALE_BLUE_GRAY /obj/item/weapon/storage/pill_bottle/kelotane - name = "bottle of kelotane pills" + name = "pill bottle (Kelotane)" desc = "Contains pills used to treat burns." starts_with = list(/obj/item/weapon/reagent_containers/pill/kelotane = 7) + wrapper_color = "#ec8b2f" /obj/item/weapon/storage/pill_bottle/spaceacillin - name = "bottle of Spaceacillin pills" + name = "pill bottle (Spaceacillin)" desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space." starts_with = list(/obj/item/weapon/reagent_containers/pill/spaceacillin = 7) + wrapper_color = COLOR_PALE_GREEN_GRAY /obj/item/weapon/storage/pill_bottle/tramadol - name = "bottle of Tramadol pills" + name = "pill bottle (Tramadol)" desc = "Contains pills used to relieve pain." starts_with = list(/obj/item/weapon/reagent_containers/pill/tramadol = 7) + wrapper_color = COLOR_PURPLE_GRAY /obj/item/weapon/storage/pill_bottle/citalopram - name = "bottle of Citalopram pills" + name = "pill bottle (Citalopram)" desc = "Contains pills used to stabilize a patient's mood." starts_with = list(/obj/item/weapon/reagent_containers/pill/citalopram = 7) + wrapper_color = COLOR_GRAY /obj/item/weapon/storage/pill_bottle/carbon - name = "bottle of Carbon pills" + name = "pill bottle (Carbon)" desc = "Contains pills used to neutralise chemicals in the stomach." starts_with = list(/obj/item/weapon/reagent_containers/pill/carbon = 7) /obj/item/weapon/storage/pill_bottle/iron - name = "bottle of Iron pills" + name = "pill bottle (Iron)" desc = "Contains pills used to aid in blood regeneration." starts_with = list(/obj/item/weapon/reagent_containers/pill/iron = 7) diff --git a/code/game/objects/items/weapons/storage/firstaid_vr.dm b/code/game/objects/items/weapons/storage/firstaid_vr.dm index 9c4e015a18..7cc415d938 100644 --- a/code/game/objects/items/weapons/storage/firstaid_vr.dm +++ b/code/game/objects/items/weapons/storage/firstaid_vr.dm @@ -1,4 +1,97 @@ +/obj/item/weapon/storage/firstaid/clotting + icon_state = "clottingkit" + +/obj/item/weapon/storage/firstaid/bonemed + icon_state = "pinky" + +/obj/item/weapon/storage/pill_bottle/adminordrazine + name = "pill bottle (Adminordrazine)" + desc = "It's magic. We don't have to explain it." + starts_with = list(/obj/item/weapon/reagent_containers/pill/adminordrazine = 21) + /obj/item/weapon/storage/pill_bottle/nutriment - name = "bottle of Nutriment pills" + name = "pill bottle (Food)" desc = "Contains pills used to feed people." - starts_with = list(/obj/item/weapon/reagent_containers/pill/nutriment = 7) + starts_with = list(/obj/item/weapon/reagent_containers/pill/nutriment = 7, /obj/item/weapon/reagent_containers/pill/protein = 7) + +/obj/item/weapon/storage/pill_bottle/rezadone + name = "pill bottle (Rezadone)" + desc = "A powder with almost magical properties, this substance can effectively treat genetic damage in humanoids, though excessive consumption has side effects." + starts_with = list(/obj/item/weapon/reagent_containers/pill/rezadone = 7) + wrapper_color = COLOR_GREEN_GRAY + +/obj/item/weapon/storage/pill_bottle/peridaxon + name = "pill bottle (Peridaxon)" + desc = "Used to encourage recovery of internal organs and nervous systems. Medicate cautiously." + starts_with = list(/obj/item/weapon/reagent_containers/pill/peridaxon = 7) + wrapper_color = COLOR_PURPLE + +/obj/item/weapon/storage/pill_bottle/carthatoline + name = "pill bottle (Carthatoline)" + desc = "Carthatoline is strong evacuant used to treat severe poisoning." + starts_with = list(/obj/item/weapon/reagent_containers/pill/carthatoline = 7) + wrapper_color = COLOR_GREEN_GRAY + +/obj/item/weapon/storage/pill_bottle/alkysine + name = "pill bottle (Alkysine)" + desc = "Alkysine is a drug used to lessen the damage to neurological tissue after a catastrophic injury. Can heal brain tissue." + starts_with = list(/obj/item/weapon/reagent_containers/pill/alkysine = 7) + wrapper_color = COLOR_YELLOW + +/obj/item/weapon/storage/pill_bottle/imidazoline + name = "pill bottle (Imidazoline)" + desc = "Heals eye damage." + starts_with = list(/obj/item/weapon/reagent_containers/pill/imidazoline = 7) + wrapper_color = COLOR_PURPLE_GRAY + +/obj/item/weapon/storage/pill_bottle/osteodaxon + name = "pill bottle (Osteodaxon)" + desc = "An experimental drug used to heal bone fractures." + starts_with = list(/obj/item/weapon/reagent_containers/pill/osteodaxon = 7) + wrapper_color = COLOR_WHITE + +/obj/item/weapon/storage/pill_bottle/myelamine + name = "pill bottle (Myelamine)" + desc = "Used to rapidly clot internal hemorrhages by increasing the effectiveness of platelets." + starts_with = list(/obj/item/weapon/reagent_containers/pill/myelamine = 7) + wrapper_color = COLOR_PALE_PURPLE_GRAY + +/obj/item/weapon/storage/pill_bottle/hyronalin + name = "pill bottle (Hyronalin)" + desc = "Hyronalin is a medicinal drug used to counter the effect of radiation poisoning." + starts_with = list(/obj/item/weapon/reagent_containers/pill/hyronalin = 7) + wrapper_color = COLOR_TEAL + +/obj/item/weapon/storage/pill_bottle/arithrazine + name = "pill bottle (Arithrazine)" + desc = "Arithrazine is an unstable medication used for the most extreme cases of radiation poisoning." + starts_with = list(/obj/item/weapon/reagent_containers/pill/arithrazine = 7) + wrapper_color = COLOR_TEAL + +/obj/item/weapon/storage/pill_bottle/corophizine + name = "pill bottle (Corophizine)" + desc = "A wide-spectrum antibiotic drug. Powerful and uncomfortable in equal doses." + starts_with = list(/obj/item/weapon/reagent_containers/pill/corophizine = 7) + wrapper_color = COLOR_PALE_GREEN_GRAY + +/obj/item/weapon/storage/pill_bottle/healing_nanites + name = "pill bottle (Healing nanites)" + desc = "Miniature medical robots that swiftly restore bodily damage." + starts_with = list(/obj/item/weapon/reagent_containers/pill/healing_nanites = 7) + +/obj/item/weapon/storage/firstaid/insiderepair + name = "combat organ kit" + desc = "Contains advanced organ medical treatments." + icon_state = "bezerk" + item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") + starts_with = list( + /obj/item/weapon/storage/pill_bottle/rezadone, + /obj/item/weapon/storage/pill_bottle/peridaxon, + /obj/item/weapon/storage/pill_bottle/carthatoline, + /obj/item/weapon/storage/pill_bottle/alkysine, + /obj/item/weapon/storage/pill_bottle/imidazoline, + /obj/item/weapon/storage/pill_bottle/osteodaxon, + /obj/item/weapon/storage/pill_bottle/myelamine, + /obj/item/weapon/storage/pill_bottle/arithrazine, + /obj/item/device/healthanalyzer/advanced + ) diff --git a/code/game/objects/items/weapons/storage/misc.dm b/code/game/objects/items/weapons/storage/misc.dm index 9dca5c3edd..b22ef4166f 100644 --- a/code/game/objects/items/weapons/storage/misc.dm +++ b/code/game/objects/items/weapons/storage/misc.dm @@ -24,3 +24,46 @@ /obj/item/weapon/storage/box/donut/empty empty = TRUE + +/obj/item/weapon/storage/box/wormcan + icon = 'icons/obj/food.dmi' + icon_state = "wormcan" + name = "can of worms" + desc = "You probably do want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/wormsickly, + /obj/item/weapon/reagent_containers/food/snacks/worm, + /obj/item/weapon/reagent_containers/food/snacks/wormdeluxe + ) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/worm = 6) + +/obj/item/weapon/storage/box/wormcan/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/box/wormcan/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty" + +/obj/item/weapon/storage/box/wormcan/sickly + icon_state = "wormcan_sickly" + name = "can of sickly worms" + desc = "You probably don't want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormsickly = 6) + +/obj/item/weapon/storage/box/wormcan/sickly/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty_sickly" + +/obj/item/weapon/storage/box/wormcan/deluxe + icon_state = "wormcan_deluxe" + name = "can of deluxe worms" + desc = "You absolutely want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe = 6) + +/obj/item/weapon/storage/box/wormcan/deluxe/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty_deluxe" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/mre.dm b/code/game/objects/items/weapons/storage/mre.dm new file mode 100644 index 0000000000..582af52f43 --- /dev/null +++ b/code/game/objects/items/weapons/storage/mre.dm @@ -0,0 +1,290 @@ +/* +MRE Stuff + */ + +/obj/item/weapon/storage/mre + name = "standard MRE" + desc = "A vacuum-sealed bag containing a day's worth of nutrients for an adult in strenuous situations. There is no visible expiration date on the package." + icon = 'icons/obj/food.dmi' + icon_state = "mre" + max_storage_space = ITEMSIZE_COST_SMALL * 6 + max_w_class = ITEMSIZE_SMALL + var/opened = FALSE + var/meal_desc = "This one is menu 1, meat pizza." + starts_with = list( + /obj/item/weapon/storage/mrebag, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/examine(mob/user) + . = ..() + to_chat(user, meal_desc) + +/obj/item/weapon/storage/mre/update_icon() + if(opened) + icon_state = "[initial(icon_state)][opened]" + . = ..() + +/obj/item/weapon/storage/mre/attack_self(mob/user) + open(user) + +/obj/item/weapon/storage/mre/open(mob/user) + if(!opened) + to_chat(usr, "You tear open the bag, breaking the vacuum seal.") + opened = 1 + update_icon() + . = ..() + +/obj/item/weapon/storage/mre/menu2 + meal_desc = "This one is menu 2, margherita." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu2, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu3 + meal_desc = "This one is menu 3, vegetable pizza." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu3, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu4 + meal_desc = "This one is menu 4, hamburger." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu4, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu5 + meal_desc = "This one is menu 5, taco." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu5, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu6 + meal_desc = "This one is menu 6, meatbread." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu6, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu7 + meal_desc = "This one is menu 7, salad." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu7, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu8 + meal_desc = " This one is menu 8, hot chili." + starts_with = list( + /obj/item/weapon/storage/mrebag/menu8, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu9 + name = "vegan MRE" + meal_desc = "This one is menu 9, boiled rice (skrell-safe)." + icon_state = "vegmre" + starts_with = list( + /obj/item/weapon/storage/mrebag/menu9, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert/menu9, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread/vegan, + /obj/random/mre/drink, + /obj/random/mre/sauce/vegan, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu10 + name = "protein MRE" + meal_desc = "This one is menu 10, protein." + icon_state = "meatmre" + starts_with = list( + /obj/item/weapon/storage/mrebag/menu10, + /obj/item/weapon/storage/mrebag/menu10, + /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/protein, + /obj/random/mre/sauce/sugarfree, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/menu11 + name = "emergency MRE" + meal_desc = "This one is menu 11, nutriment paste. Only for emergencies." + icon_state = "crayonmre" + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/liquidfood, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood, + /obj/item/weapon/reagent_containers/food/snacks/liquidprotein, + /obj/item/weapon/reagent_containers/food/snacks/liquidprotein, + ) + +/obj/item/weapon/storage/mre/menu12 + name = "crayon MRE" + meal_desc = "This one doesn't have a menu listing. How very odd." + icon_state = "crayonmre" + starts_with = list( + /obj/item/weapon/storage/fancy/crayons, + /obj/item/weapon/storage/mrebag/dessert/menu11, + /obj/random/mre/sauce/crayon, + /obj/random/mre/sauce/crayon, + /obj/random/mre/sauce/crayon + ) + +/obj/item/weapon/storage/mre/menu13 + name = "medical MRE" + meal_desc = "This one is menu 13, vitamin paste & dessert. Only for emergencies." + icon_state = "crayonmre" + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/liquidvitamin, + /obj/item/weapon/reagent_containers/food/snacks/liquidvitamin, + /obj/item/weapon/reagent_containers/food/snacks/liquidvitamin, + /obj/item/weapon/reagent_containers/food/snacks/liquidprotein, + /obj/random/mre/drink, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mre/random + meal_desc = "The menu label is faded out." + starts_with = list( + /obj/random/mre/main, + /obj/item/weapon/storage/mrebag/side, + /obj/item/weapon/storage/mrebag/dessert, + /obj/item/weapon/storage/fancy/crackers, + /obj/random/mre/spread, + /obj/random/mre/drink, + /obj/random/mre/sauce, + /obj/item/weapon/material/kitchen/utensil/spoon/plastic + ) + +/obj/item/weapon/storage/mrebag + name = "main course" + desc = "A vacuum-sealed bag containing the MRE's main course. Self-heats when opened." + icon = 'icons/obj/food.dmi' + icon_state = "pouch_medium" + storage_slots = 1 + w_class = ITEMSIZE_SMALL + max_w_class = ITEMSIZE_SMALL + var/opened = FALSE + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/slice/meatpizza/filled) + +/obj/item/weapon/storage/mrebag/Initialize() + . = ..() + +/obj/item/weapon/storage/mrebag/update_icon() + if(opened) + icon_state = "[initial(icon_state)][opened]" + . = ..() + +/obj/item/weapon/storage/mrebag/attack_self(mob/user) + open(user) + +/obj/item/weapon/storage/mrebag/open(mob/user) + if(!opened) + to_chat(usr, "The pouch heats up as you break the vaccum seal.") + opened = 1 + update_icon() + . = ..() + +/obj/item/weapon/storage/mrebag/menu2 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/slice/margherita/filled) + +/obj/item/weapon/storage/mrebag/menu3 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/slice/vegetablepizza/filled) + +/obj/item/weapon/storage/mrebag/menu4 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeyburger) + +/obj/item/weapon/storage/mrebag/menu5 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/taco) + +/obj/item/weapon/storage/mrebag/menu6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/slice/meatbread/filled) + +/obj/item/weapon/storage/mrebag/menu7 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/tossedsalad) + +/obj/item/weapon/storage/mrebag/menu8 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/hotchili) + +/obj/item/weapon/storage/mrebag/menu9 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/boiledrice) + +/obj/item/weapon/storage/mrebag/menu10 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/meatcube) + +/obj/item/weapon/storage/mrebag/side + name = "side dish" + desc = "A vacuum-sealed bag containing the MRE's side dish. Self-heats when opened." + icon_state = "pouch_small" + starts_with = list(/obj/random/mre/side) + +/obj/item/weapon/storage/mrebag/side/menu10 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/meatcube) + +/obj/item/weapon/storage/mrebag/dessert + name = "dessert" + desc = "A vacuum-sealed bag containing the MRE's dessert." + icon_state = "pouch_small" + starts_with = list(/obj/random/mre/dessert) + +/obj/item/weapon/storage/mrebag/dessert/menu9 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/plumphelmetbiscuit) + +/obj/item/weapon/storage/mrebag/dessert/menu11 + starts_with = list(/obj/item/weapon/pen/crayon/rainbow) diff --git a/code/game/objects/items/weapons/storage/toolbox_vr.dm b/code/game/objects/items/weapons/storage/toolbox_vr.dm new file mode 100644 index 0000000000..f65731091a --- /dev/null +++ b/code/game/objects/items/weapons/storage/toolbox_vr.dm @@ -0,0 +1,10 @@ +/obj/item/weapon/storage/toolbox/lunchbox/survival + name = "survival lunchbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "lunchbox_survival" + item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") + desc = "A little lunchbox. This one seems to be much sturdier than normal, made of a durable steel!" + max_storage_space = ITEMSIZE_COST_SMALL * 6 + +/obj/item/weapon/storage/toolbox/lunchbox/survival/zaddat + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 6) \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index 4291752587..9624a85ca2 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -93,6 +93,42 @@ O.update() . = ..() +/obj/item/weapon/storage/box/syndie_kit/imp_aug + name = "boxed augment implant (with injector)" + var/case_type = /obj/item/weapon/implantcase/shades + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/Initialize() + new /obj/item/weapon/implanter(src) + new case_type(src) + . = ..() + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/taser + case_type = /obj/item/weapon/implantcase/taser + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/laser + case_type = /obj/item/weapon/implantcase/laser + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/dart + case_type = /obj/item/weapon/implantcase/dart + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/toolkit + case_type = /obj/item/weapon/implantcase/toolkit + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/medkit + case_type = /obj/item/weapon/implantcase/medkit + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/surge + case_type = /obj/item/weapon/implantcase/surge + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/analyzer + case_type = /obj/item/weapon/implantcase/analyzer + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/sword + case_type = /obj/item/weapon/implantcase/sword + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/sprinter + case_type = /obj/item/weapon/implantcase/sprinter + /obj/item/weapon/storage/box/syndie_kit/space name = "boxed space suit and helmet" starts_with = list( diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index ce1227014c..95db86460e 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -35,7 +35,8 @@ /obj/item/weapon/tool/screwdriver, /obj/item/weapon/stamp, /obj/item/clothing/accessory/permit, - /obj/item/clothing/accessory/badge + /obj/item/clothing/accessory/badge, + /obj/item/weapon/makeover ) cant_hold = list(/obj/item/weapon/tool/screwdriver/power) slot_flags = SLOT_ID diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 53c092d923..371082f1f6 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -9,6 +9,7 @@ sharp = 0 edge = 0 throwforce = 7 + flags = NOCONDUCT w_class = ITEMSIZE_NORMAL origin_tech = list(TECH_COMBAT = 2) attack_verb = list("beaten") @@ -18,6 +19,7 @@ var/status = 0 //whether the thing is on or not var/obj/item/weapon/cell/bcell = null var/hitcost = 240 + var/use_external_power = FALSE //only used to determine if it's a cyborg baton /obj/item/weapon/melee/baton/New() ..() @@ -108,6 +110,8 @@ user <<"The baton does not have a power source installed." /obj/item/weapon/melee/baton/attackby(obj/item/weapon/W, mob/user) + if(use_external_power) + return if(istype(W, /obj/item/weapon/cell)) if(istype(W, /obj/item/weapon/cell/device)) if(!bcell) @@ -136,6 +140,11 @@ return ..() /obj/item/weapon/melee/baton/attack_self(mob/user) + if(use_external_power) + //try to find our power cell + var/mob/living/silicon/robot/R = loc + if (istype(R)) + bcell = R.cell if(bcell && bcell.charge > hitcost) status = !status user << "[src] is now [status ? "on" : "off"]." @@ -204,16 +213,7 @@ //secborg stun baton module /obj/item/weapon/melee/baton/robot hitcost = 500 - -/obj/item/weapon/melee/baton/robot/attack_self(mob/user) - //try to find our power cell - var/mob/living/silicon/robot/R = loc - if (istype(R)) - bcell = R.cell - return ..() - -/obj/item/weapon/melee/baton/robot/attackby(obj/item/weapon/W, mob/user) - return + use_external_power = TRUE //Makeshift stun baton. Replacement for stun gloves. /obj/item/weapon/melee/baton/cattleprod @@ -274,13 +274,4 @@ // Borg version, for the lost module. /obj/item/weapon/melee/baton/shocker/robot - -/obj/item/weapon/melee/baton/shocker/robot/attack_self(mob/user) - //try to find our power cell - var/mob/living/silicon/robot/R = loc - if (istype(R)) - bcell = R.cell - return ..() - -/obj/item/weapon/melee/baton/shocker/robot/attackby(obj/item/weapon/W, mob/user) - return \ No newline at end of file + use_external_power = TRUE \ No newline at end of file diff --git a/code/game/objects/items/weapons/surgery_tools.dm b/code/game/objects/items/weapons/surgery_tools.dm index f011cd917b..292f047f0f 100644 --- a/code/game/objects/items/weapons/surgery_tools.dm +++ b/code/game/objects/items/weapons/surgery_tools.dm @@ -128,6 +128,15 @@ icon_state = "scalpel_manager_on" force = 7.5 +/obj/item/weapon/surgical/scalpel/ripper + name = "organ pincers" + desc = "A horrifying bladed tool with a large metal spike in its center. The tool is used for rapidly removing organs from hopefully willing patients." + icon_state = "organ_ripper" + item_state = "bone_setter" + force = 15.0 + toolspeed = 0.75 + origin_tech = list(TECH_MATERIAL = 5, TECH_BIO = 3, TECH_ILLEGAL = 2) + /* * Circular Saw */ @@ -147,6 +156,19 @@ sharp = 1 edge = 1 +/obj/item/weapon/surgical/circular_saw/manager + name = "energetic bone diverter" + desc = "For heavy duty cutting (and sealing), with science!" + icon_state = "adv_saw" + item_state = "saw3" + hitsound = 'sound/weapons/emitter2.ogg' + damtype = SEARING + w_class = ITEMSIZE_LARGE + origin_tech = list(TECH_BIO = 4, TECH_MATERIAL = 6, TECH_MAGNET = 6) + matter = list(DEFAULT_WALL_MATERIAL = 12500) + attack_verb = list("attacked", "slashed", "seared", "cut") + toolspeed = 0.75 + //misc, formerly from code/defines/weapons.dm /obj/item/weapon/surgical/bonegel name = "bone gel" @@ -245,4 +267,4 @@ /obj/item/weapon/surgical/bone_clamp/alien icon = 'icons/obj/abductor.dmi' - toolspeed = 0.75 \ No newline at end of file + toolspeed = 0.75 diff --git a/code/game/objects/items/weapons/tools/crowbar.dm b/code/game/objects/items/weapons/tools/crowbar.dm index 73b62f0741..fab0a6fb98 100644 --- a/code/game/objects/items/weapons/tools/crowbar.dm +++ b/code/game/objects/items/weapons/tools/crowbar.dm @@ -64,7 +64,7 @@ /obj/item/weapon/tool/crowbar/hybrid/is_crowbar() if(prob(10)) var/turf/T = get_turf(src) - radiation_repository.radiate(get_turf(src), 5) + SSradiation.radiate(get_turf(src), 5) T.visible_message("\The [src] shudders!") return FALSE return TRUE diff --git a/code/game/objects/items/weapons/tools/screwdriver.dm b/code/game/objects/items/weapons/tools/screwdriver.dm index a9bdd6cee8..1969987ff9 100644 --- a/code/game/objects/items/weapons/tools/screwdriver.dm +++ b/code/game/objects/items/weapons/tools/screwdriver.dm @@ -108,7 +108,7 @@ /obj/item/weapon/tool/screwdriver/hybrid/is_screwdriver() if(prob(10)) var/turf/T = get_turf(src) - radiation_repository.radiate(get_turf(src), 5) + SSradiation.radiate(get_turf(src), 5) T.visible_message("\The [src] shudders!") return FALSE return TRUE diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 3a969562f8..33c5a75078 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -504,6 +504,14 @@ mounted_pack.return_nozzle() to_chat(user, "\The [src] retracts to its fueltank.") +/obj/item/weapon/weldingtool/tubefed/survival + name = "tube-fed emergency welding tool" + desc = "A bulky, cooler-burning welding tool that draws from a worn welding tank." + icon_state = "tubewelder" + max_fuel = 5 + toolspeed = 1.75 + eye_safety_modifier = 2 + /* * Electric/Arc Welder */ diff --git a/code/game/objects/items/weapons/tools/wirecutters.dm b/code/game/objects/items/weapons/tools/wirecutters.dm index 181c786c4c..4d61609db4 100644 --- a/code/game/objects/items/weapons/tools/wirecutters.dm +++ b/code/game/objects/items/weapons/tools/wirecutters.dm @@ -88,7 +88,7 @@ /obj/item/weapon/tool/wirecutters/hybrid/is_wirecutter() if(prob(10)) var/turf/T = get_turf(src) - radiation_repository.radiate(get_turf(src), 5) + SSradiation.radiate(get_turf(src), 5) T.visible_message("\The [src] shudders!") return FALSE return TRUE diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm index 652e32cf75..3f02a2f8b3 100644 --- a/code/game/objects/items/weapons/tools/wrench.dm +++ b/code/game/objects/items/weapons/tools/wrench.dm @@ -44,7 +44,7 @@ /obj/item/weapon/tool/wrench/hybrid/is_wrench() if(prob(10)) var/turf/T = get_turf(src) - radiation_repository.radiate(get_turf(src), 5) + SSradiation.radiate(get_turf(src), 5) T.visible_message("\The [src] shudders!") return FALSE return TRUE diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 900887ce11..1febf6b1ae 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -11,6 +11,8 @@ origin_tech = list(TECH_MATERIAL = 1) matter = list(DEFAULT_WALL_MATERIAL = 18750) var/deployed = 0 + var/camo_net = FALSE + var/stun_length = 0.25 SECONDS /obj/item/weapon/beartrap/suicide_act(mob/user) var/datum/gender/T = gender_datums[user.get_visible_gender()] @@ -98,6 +100,7 @@ set_dir(L.dir) can_buckle = 1 buckle_mob(L) + L.Stun(stun_length) L << "The steel jaws of \the [src] bite into you, trapping you in place!" deployed = 0 can_buckle = initial(can_buckle) @@ -128,6 +131,21 @@ ..() if(!deployed) + if(camo_net) + alpha = 255 + icon_state = "beartrap0" else + if(camo_net) + alpha = 50 + icon_state = "beartrap1" + +/obj/item/weapon/beartrap/hunting + name = "hunting trap" + desc = "A mechanically activated leg trap. High-tech and reliable. Looks like it could really hurt if you set it off." + stun_length = 1 SECOND + camo_net = TRUE + color = "#C9DCE1" + + origin_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_MAGNET = 4, TECH_PHORON = 2, TECH_ARCANE = 1) diff --git a/code/game/objects/items/weapons/weldbackpack.dm b/code/game/objects/items/weapons/weldbackpack.dm index 5df0c9dbe3..4225ed810a 100644 --- a/code/game/objects/items/weapons/weldbackpack.dm +++ b/code/game/objects/items/weapons/weldbackpack.dm @@ -7,6 +7,7 @@ w_class = ITEMSIZE_LARGE var/max_fuel = 350 var/obj/item/weapon/nozzle = null //Attached welder, or other spray device. + var/nozzle_type = /obj/item/weapon/weldingtool/tubefed var/nozzle_attached = 0 /obj/item/weapon/weldpack/Initialize() @@ -15,7 +16,7 @@ reagents = R R.my_atom = src R.add_reagent("fuel", max_fuel) - nozzle = new/obj/item/weapon/weldingtool/tubefed(src) + nozzle = new nozzle_type(src) nozzle_attached = 1 /obj/item/weapon/weldpack/Destroy() @@ -145,3 +146,14 @@ ..(user) user << text("\icon[] [] units of fuel left!", src, src.reagents.total_volume) return + +/obj/item/weapon/weldpack/survival + name = "emergency welding kit" + desc = "A heavy-duty, portable welding fluid carrier." + slot_flags = SLOT_BACK + icon = 'icons/obj/storage.dmi' + icon_state = "welderpack-e" + item_state = "welderpack" + w_class = ITEMSIZE_LARGE + max_fuel = 100 + nozzle_type = /obj/item/weapon/weldingtool/tubefed/survival diff --git a/code/game/objects/mob_spawner_vr.dm b/code/game/objects/mob_spawner_vr.dm index 0422c408f8..23ec38c907 100644 --- a/code/game/objects/mob_spawner_vr.dm +++ b/code/game/objects/mob_spawner_vr.dm @@ -84,7 +84,7 @@ if(destructible) take_damage(Proj.get_structure_damage()) -/obj/structure/mob_spawner/proc/take_damage(var/damage) +/obj/structure/mob_spawner/take_damage(var/damage) health -= damage if(health <= 0) visible_message("\The [src] breaks apart!") diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 999b5fca99..c7b8c07f8d 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -7,6 +7,7 @@ var/unacidable = 0 //universal "unacidabliness" var, here so you can use it in any obj. animate_movement = 2 var/throwforce = 1 + var/catchable = 1 // can it be caught on throws/flying? var/sharp = 0 // whether this object cuts var/edge = 0 // whether this object is more likely to dismember var/pry = 0 //Used in attackby() to open doors @@ -54,7 +55,7 @@ CouldNotUseTopic(usr) return 1 -/obj/CanUseTopic(var/mob/user, var/datum/topic_state/state) +/obj/CanUseTopic(var/mob/user, var/datum/topic_state/state = default_state) if(user.CanUseObjTopic(src)) return ..() to_chat(user, "\icon[src]Access Denied!") @@ -172,6 +173,9 @@ */ return +/obj/proc/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) // Saycode gets worse every day. + return FALSE + /obj/proc/see_emote(mob/M as mob, text, var/emote_type) return diff --git a/code/game/objects/random/_random.dm b/code/game/objects/random/_random.dm index 8a146f82c4..b559495eb5 100644 --- a/code/game/objects/random/_random.dm +++ b/code/game/objects/random/_random.dm @@ -11,7 +11,15 @@ . = ..() if (!prob(spawn_nothing_percentage)) spawn_item() + Random_SafeDestroy(0) + +// This function should, theoretically, guarantee the deletion of the random object. Not all of them destroy themselves for some reason, especially if created through non-standard means. +/obj/random/proc/Random_SafeDestroy(var/recursion_level) + set waitfor = FALSE + sleep(30) qdel(src) + if(src && recursion_level < 5) + Random_SafeDestroy(recursion_level + 1) // this function should return a specific item to spawn /obj/random/proc/item_to_spawn() diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm index aba62d0806..d39983af2f 100644 --- a/code/game/objects/random/mapping.dm +++ b/code/game/objects/random/mapping.dm @@ -65,11 +65,31 @@ prob(10);/obj/effect/mine/stun, prob(10);/obj/effect/mine/incendiary,) +/obj/random/humanoidremains + name = "Random Humanoid Remains" + desc = "This is a random pile of remains." + spawn_nothing_percentage = 15 + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + +/obj/random/humanoidremains/item_to_spawn() + return pick(prob(30);/obj/effect/decal/remains/human, + prob(25);/obj/effect/decal/remains/ribcage, + prob(25);/obj/effect/decal/remains/tajaran, + prob(10);/obj/effect/decal/remains/unathi, + prob(10);/obj/effect/decal/remains/posi + ) + /obj/random_multi/single_item/captains_spare_id name = "Multi Point - Captain's Spare" id = "Captain's spare id" item_path = /obj/item/weapon/card/id/gold/captain/spare +/obj/random_multi/single_item/hand_tele + name = "Multi Point - Hand Teleporter" + id = "hand tele" + item_path = /obj/item/weapon/hand_tele + /obj/random_multi/single_item/sfr_headset name = "Multi Point - headset" id = "SFR headset" @@ -391,5 +411,54 @@ /obj/item/clothing/mask/luchador/rudos, /obj/item/clothing/mask/luchador/tecnicos, /obj/structure/closet/crate + ), + prob(1);list( + /obj/machinery/artifact, + /obj/structure/anomaly_container + ), + prob(1);list( + /obj/random/curseditem, + /obj/random/humanoidremains, + /obj/structure/closet/crate ) ) + +/* + * Turf swappers. + */ + +/obj/random/turf + name = "random Sif turf" + desc = "This is a random Sif turf." + + spawn_nothing_percentage = 20 + + var/override_outdoors = FALSE // Do we override our chosen turf's outdoors? + var/turf_outdoors = TRUE // Will our turf be outdoors? + +/obj/random/turf/spawn_item() + var/build_path = item_to_spawn() + + var/turf/T1 = get_turf(src) + T1.ChangeTurf(build_path, 1, 1, FALSE) + + if(override_outdoors) + T1.outdoors = turf_outdoors + +/obj/random/turf/item_to_spawn() + return pick(prob(25);/turf/simulated/floor/outdoors/grass/sif, + prob(25);/turf/simulated/floor/outdoors/dirt, + prob(25);/turf/simulated/floor/outdoors/grass/sif/forest, + prob(25);/turf/simulated/floor/outdoors/rocks) + +/obj/random/turf/lava + name = "random Lava spawn" + desc = "This is a random lava spawn." + + override_outdoors = TRUE + turf_outdoors = FALSE + +/obj/random/turf/lava/item_to_spawn() + return pick(prob(5);/turf/simulated/floor/lava, + prob(3);/turf/simulated/floor/outdoors/rocks/caves, + prob(1);/turf/simulated/mineral) diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm index 0f9f85582f..f9180c99f1 100644 --- a/code/game/objects/random/misc.dm +++ b/code/game/objects/random/misc.dm @@ -519,4 +519,171 @@ /obj/effect/decal/cleanable/ash, /obj/item/weapon/cigbutt, /obj/item/weapon/cigbutt/cigarbutt, - /obj/effect/decal/remains/mouse) \ No newline at end of file + /obj/effect/decal/remains/mouse) + +/obj/random/janusmodule + name = "random janus circuit" + desc = "A random (possibly broken) Janus module." + icon = 'icons/obj/abductor.dmi' + icon_state = "circuit_damaged" + +/obj/random/janusmodule/item_to_spawn() + return pick(subtypesof(/obj/item/weapon/circuitboard/mecha/imperion)) + +/obj/random/curseditem + name = "random cursed item" + desc = "For use in dungeons." + icon = 'icons/obj/storage.dmi' + icon_state = "red" + +/obj/random/curseditem/item_to_spawn() + var/possible_object_paths = list(/obj/item/weapon/paper/carbon/cursedform) + possible_object_paths |= subtypesof(/obj/item/clothing/head/psy_crown) + return pick(possible_object_paths) + +//Random MRE stuff + +/obj/random/mre + name = "random MRE" + desc = "This is a random single MRE." + icon = 'icons/obj/food.dmi' + icon_state = "mre" + drop_get_turf = FALSE + +/obj/random/mre/item_to_spawn() + return pick(/obj/item/weapon/storage/mre, + /obj/item/weapon/storage/mre/menu2, + /obj/item/weapon/storage/mre/menu3, + /obj/item/weapon/storage/mre/menu4, + /obj/item/weapon/storage/mre/menu5, + /obj/item/weapon/storage/mre/menu6, + /obj/item/weapon/storage/mre/menu7, + /obj/item/weapon/storage/mre/menu8, + /obj/item/weapon/storage/mre/menu9, + /obj/item/weapon/storage/mre/menu10) + + +/obj/random/mre/main + name = "random MRE main course" + desc = "This is a random main course for MREs." + icon_state = "pouch" + drop_get_turf = FALSE + +/obj/random/mre/main/item_to_spawn() + return pick(/obj/item/weapon/storage/mrebag, + /obj/item/weapon/storage/mrebag/menu2, + /obj/item/weapon/storage/mrebag/menu3, + /obj/item/weapon/storage/mrebag/menu4, + /obj/item/weapon/storage/mrebag/menu5, + /obj/item/weapon/storage/mrebag/menu6, + /obj/item/weapon/storage/mrebag/menu7, + /obj/item/weapon/storage/mrebag/menu8) + +/obj/random/mre/side + name = "random MRE side dish" + desc = "This is a random side dish for MREs." + icon_state = "pouch" + drop_get_turf = FALSE + +/obj/random/mre/side/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/snacks/tossedsalad, + /obj/item/weapon/reagent_containers/food/snacks/boiledrice, + /obj/item/weapon/reagent_containers/food/snacks/poppypretzel, + /obj/item/weapon/reagent_containers/food/snacks/twobread, + /obj/item/weapon/reagent_containers/food/snacks/jelliedtoast) + +/obj/random/mre/dessert + name = "random MRE dessert" + desc = "This is a random dessert for MREs." + icon_state = "pouch" + drop_get_turf = FALSE + +/obj/random/mre/dessert/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/snacks/candy, + /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, + /obj/item/weapon/reagent_containers/food/snacks/donut/normal, + /obj/item/weapon/reagent_containers/food/snacks/donut/cherryjelly, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar, + /obj/item/weapon/reagent_containers/food/snacks/cookie) + +/obj/random/mre/dessert/vegan + name = "random vegan MRE dessert" + desc = "This is a random vegan dessert for MREs." + +/obj/random/mre/dessert/vegan/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/snacks/candy, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar, + /obj/item/weapon/reagent_containers/food/snacks/donut/cherryjelly, + /obj/item/weapon/reagent_containers/food/snacks/plumphelmetbiscuit) + +/obj/random/mre/drink + name = "random MRE drink" + desc = "This is a random drink for MREs." + icon_state = "packet" + drop_get_turf = FALSE + +/obj/random/mre/drink/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/coffee, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/tea, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/cocoa, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/grape, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/orange, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/watermelon, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/apple) + +/obj/random/mre/spread + name = "random MRE spread" + desc = "This is a random spread packet for MREs." + icon_state = "packet" + drop_get_turf = FALSE + +/obj/random/mre/spread/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/jelly, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/honey) + +/obj/random/mre/spread/vegan + name = "random vegan MRE spread" + desc = "This is a random vegan spread packet for MREs" + +/obj/random/mre/spread/vegan/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/jelly) + +/obj/random/mre/sauce + name = "random MRE sauce" + desc = "This is a random sauce packet for MREs." + icon_state = "packet" + drop_get_turf = FALSE + +/obj/random/mre/sauce/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/salt, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/pepper, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/sugar, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/capsaicin, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/ketchup, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/mayo, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/soy) + +/obj/random/mre/sauce/vegan/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/salt, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/pepper, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/sugar, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/soy) + +/obj/random/mre/sauce/sugarfree/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/salt, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/pepper, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/capsaicin, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/ketchup, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/mayo, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/soy) + +/obj/random/mre/sauce/crayon/item_to_spawn() + return pick(/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/generic, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/red, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/orange, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/yellow, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/green, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/blue, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/purple, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/grey, + /obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/brown) diff --git a/code/game/objects/random/mob_vr.dm b/code/game/objects/random/mob_vr.dm index 62e3e376c7..c1ce7aaf15 100644 --- a/code/game/objects/random/mob_vr.dm +++ b/code/game/objects/random/mob_vr.dm @@ -229,3 +229,6 @@ prob(50);/obj/random/soap, prob(60);/obj/random/drinkbottle, prob(500);/obj/random/maintenance/clean) + +/obj/random/action_figure/supplypack + drop_get_turf = FALSE \ No newline at end of file diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 6173bac9b4..5c606422d7 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -104,7 +104,7 @@ return 0 return 1 -/obj/structure/catwalk/proc/take_damage(amount) +/obj/structure/catwalk/take_damage(amount) health -= amount if(health <= 0) visible_message("\The [src] breaks down!") diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 66c219293e..95d38c4473 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -385,7 +385,7 @@ if(!opened) icon_state = icon_closed if(sealed) - overlays += "sealed" + overlays += "welded" else icon_state = icon_opened @@ -468,3 +468,10 @@ if(istype(src.loc, /obj/structure/closet)) return (loc.return_air_for_internal_lifeform(L)) return return_air() + +/obj/structure/closet/take_damage(var/damage) + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) + return + dump_contents() + spawn(1) qdel(src) + return 1 \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo_vr.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo_vr.dm new file mode 100644 index 0000000000..332b629d7b --- /dev/null +++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo_vr.dm @@ -0,0 +1,3 @@ +/obj/structure/closet/secure_closet/miner/Initialize() + starts_with += /obj/item/device/gps/mining + return ..() \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 8380c059ec..43f50a8387 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -11,6 +11,8 @@ starts_with = list( /obj/item/clothing/accessory/storage/brown_vest, /obj/item/blueprints, + ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, + ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, /obj/item/clothing/under/rank/chief_engineer, /obj/item/clothing/under/rank/chief_engineer/skirt, /obj/item/clothing/head/hardhat/white, @@ -95,6 +97,7 @@ /obj/item/clothing/glasses/meson, /obj/item/weapon/cartridge/engineering, /obj/item/taperoll/engineering, + /obj/item/clothing/head/hardhat, /obj/item/clothing/suit/storage/hooded/wintercoat/engineering, /obj/item/clothing/shoes/boots/winter/engineering, /obj/item/weapon/tank/emergency/oxygen/engi, @@ -123,8 +126,10 @@ starts_with = list( /obj/item/clothing/accessory/storage/brown_vest, /obj/item/clothing/suit/fire/firefighter, + /obj/item/clothing/head/hardhat/red, /obj/item/device/flashlight, /obj/item/weapon/extinguisher, + ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, /obj/item/device/radio/headset/headset_eng, /obj/item/device/radio/headset/headset_eng/alt, /obj/item/clothing/suit/storage/hazardvest, diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical_vr.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical_vr.dm new file mode 100644 index 0000000000..e47f835891 --- /dev/null +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical_vr.dm @@ -0,0 +1,3 @@ +/obj/structure/closet/secure_closet/paramedic/Initialize() + starts_with += /obj/item/device/gps/medical + return ..() \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 368fbf10f4..350e9dc514 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -302,7 +302,7 @@ GLOBAL_LIST_BOILERPLATE(all_brig_closets, /obj/structure/closet/secure_closet/br var/id = null starts_with = list( - /obj/item/clothing/under/color/orange, + /obj/item/clothing/under/color/prison, /obj/item/clothing/shoes/orange) diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index 9bdc9659e2..916c12f643 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -140,7 +140,7 @@ icon_closed = "orange" starts_with = list( - /obj/item/clothing/under/color/orange = 3, + /obj/item/clothing/under/color/prison = 3, /obj/item/clothing/shoes/orange = 3) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index e399c04d0b..6517dda8f8 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -3,7 +3,7 @@ /obj/structure/closet/crate name = "crate" desc = "A rectangular steel crate." - icon = 'icons/obj/storage.dmi' + icon = 'icons/obj/storage_vr.dmi' //VOREStation edit icon_state = "crate" icon_opened = "crateopen" icon_closed = "crate" @@ -293,6 +293,9 @@ /obj/structure/closet/crate/solar name = "solar pack crate" + icon_state = "engi_crate" //VOREStation Edit + icon_opened = "engi_crateopen" //VOREStation Edit + icon_closed = "engi_crate" //VOREStation Edit starts_with = list( /obj/item/solar_assembly = 21, @@ -343,12 +346,13 @@ desc = "A crate of emergency rations." starts_with = list( - /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 4) + /obj/random/mre = 6) /obj/structure/closet/crate/bin name = "large bin" desc = "A large bin." + icon = 'icons/obj/storage.dmi' //VOREStation edit icon_state = "largebin" icon_opened = "largebinopen" icon_closed = "largebin" @@ -417,6 +421,7 @@ /obj/structure/closet/crate/secure/bin name = "secure bin" desc = "A secure bin." + icon = 'icons/obj/storage.dmi' //VOREStation edit icon_state = "largebins" icon_opened = "largebinsopen" icon_closed = "largebins" @@ -429,7 +434,7 @@ /obj/structure/closet/crate/large name = "large crate" desc = "A hefty metal crate." - icon = 'icons/obj/storage.dmi' + icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit icon_state = "largemetal" icon_opened = "largemetalopen" icon_closed = "largemetal" @@ -457,10 +462,10 @@ /obj/structure/closet/crate/secure/large name = "large crate" desc = "A hefty metal crate with an electronic locking system." - icon = 'icons/obj/storage.dmi' - icon_state = "largemetal" - icon_opened = "largemetalopen" - icon_closed = "largemetal" + icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit + icon_state = "largemetalsecure" //VOREStation Edit + icon_opened = "largemetalsecureopen" //VOREStation Edit + icon_closed = "largemetalsecure" //VOREStation Edit redlight = "largemetalr" greenlight = "largemetalg" diff --git a/code/game/objects/structures/crates_lockers/crates_vr.dm b/code/game/objects/structures/crates_lockers/crates_vr.dm index c2a5cee915..85eccb3d77 100644 --- a/code/game/objects/structures/crates_lockers/crates_vr.dm +++ b/code/game/objects/structures/crates_lockers/crates_vr.dm @@ -31,4 +31,9 @@ ..() - return \ No newline at end of file + return + +/obj/structure/closet/crate/medical/blood + icon_state = "blood" + icon_opened = "bloodopen" + icon_closed = "blood" \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index a7a4b77823..79bd5e2949 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -1,7 +1,7 @@ /obj/structure/largecrate name = "large crate" desc = "A hefty wooden crate." - icon = 'icons/obj/storage.dmi' + icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit icon_state = "densecrate" density = 1 var/list/starts_with @@ -82,10 +82,11 @@ starts_with = list(/obj/structure/vehiclecage/quadtrailer) /obj/structure/largecrate/animal - icon_state = "mulecrate" + icon_state = "lisacrate" //VOREStation Edit /obj/structure/largecrate/animal/mulebot name = "Mulebot crate" + icon_state = "mulecrate" //VOREStation Edit starts_with = list(/mob/living/bot/mulebot) /obj/structure/largecrate/animal/corgi diff --git a/code/game/objects/structures/crates_lockers/largecrate_vr.dm b/code/game/objects/structures/crates_lockers/largecrate_vr.dm index c151409e62..6886a08a3c 100644 --- a/code/game/objects/structures/crates_lockers/largecrate_vr.dm +++ b/code/game/objects/structures/crates_lockers/largecrate_vr.dm @@ -50,7 +50,7 @@ /mob/living/simple_mob/animal/wolf, /mob/living/simple_mob/animal/space/bear;0.5, /mob/living/simple_mob/animal/space/carp, - /mob/living/simple_mob/animal/space/mimic, + /mob/living/simple_mob/vore/aggressive/mimic, /mob/living/simple_mob/vore/aggressive/rat, /mob/living/simple_mob/vore/aggressive/rat/tame, // /mob/living/simple_mob/otie;0.5 diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index a5ad07213d..15bfe8ac89 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -1,14 +1,82 @@ +/obj/structure/flora + name = "flora" + desc = "A perfectly generic plant." + anchored = TRUE // Usually, plants don't move. Usually. + plane = DECAL_PLANE + layer = BELOW_MOB_LAYER + var/randomize_size = FALSE + var/max_x_scale = 1.25 + var/max_y_scale = 1.25 + var/min_x_scale = 0.9 + var/min_y_scale = 0.9 + var/harvest_tool = null // The type of item used to harvest the plant. + var/harvest_count = 0 + + var/randomize_harvest_count = TRUE + var/max_harvests = 0 + var/min_harvests = -1 + var/list/harvest_loot = null // Should be an associative list for things to spawn, and their weights. An example would be a branch from a tree. + +/obj/structure/flora/Initialize() + ..() + + if(randomize_size) + icon_scale_x = rand(min_x_scale * 100, max_x_scale * 100) / 100 + icon_scale_y = rand(min_y_scale * 100, max_y_scale * 100) / 100 + + if(prob(50)) + icon_scale_x *= -1 + update_transform() + + if(randomize_harvest_count) + max_harvests = max(0, rand(min_harvests, max_harvests)) // Incase you want to weight it more toward 'not harvestable', set min_harvests to a negative value. + +/obj/structure/flora/examine(mob/user) + . = ..(user) + if(harvest_count < max_harvests) + to_chat(user, "\The [src] seems to have something hanging from it.") + +/obj/structure/flora/attackby(var/obj/item/weapon/W, var/mob/living/user) + if(can_harvest(W)) + var/harvest_spawn = pickweight(harvest_loot) + var/atom/movable/AM = spawn_harvest(harvest_spawn, user) + + if(!AM) + to_chat(user, "You fail to harvest anything from \the [src].") + + else + to_chat(user, "You harvest \the [AM] from \the [src].") + return + + ..(W, user) + +/obj/structure/flora/proc/can_harvest(var/obj/item/I) + . = FALSE + if(harvest_tool && istype(I, harvest_tool) && harvest_loot && harvest_loot.len && harvest_count < max_harvests) + . = TRUE + return . + +/obj/structure/flora/proc/spawn_harvest(var/path = null, var/mob/user = null) + if(!ispath(path)) + return 0 + var/turf/Target = get_turf(src) + if(user) + Target = get_turf(user) + + var/atom/movable/AM = new path(Target) + + harvest_count++ + return AM //bushes /obj/structure/flora/bush name = "bush" icon = 'icons/obj/flora/snowflora.dmi' icon_state = "snowbush1" - anchored = 1 /obj/structure/flora/bush/New() ..() @@ -20,6 +88,7 @@ icon = 'icons/obj/plants.dmi' icon_state = "plant-26" + anchored = FALSE //newbushes @@ -27,7 +96,6 @@ name = "bush" icon = 'icons/obj/flora/ausflora.dmi' icon_state = "firstbush_1" - anchored = 1 /obj/structure/flora/ausbushes/New() ..() @@ -122,7 +190,7 @@ /obj/structure/flora/ausbushes/ppflowers/New() ..() - icon_state = "ppflowers_[rand(1, 4)]" + icon_state = "ppflowers_[rand(1, 3)]" /obj/structure/flora/ausbushes/sparsegrass icon_state = "sparsegrass_1" @@ -144,6 +212,7 @@ icon_state = "hangskele" desc = "It's an anatomical model of a human skeletal system made of plaster." + plane = OBJ_PLANE //potted plants credit: Flashkirby /obj/structure/flora/pottedplant @@ -152,6 +221,8 @@ icon = 'icons/obj/plants.dmi' icon_state = "plant-01" + plane = OBJ_PLANE + /obj/structure/flora/pottedplant/large name = "large potted plant" desc = "This is a large plant. Three branches support pairs of waxy leaves." @@ -298,6 +369,14 @@ /obj/structure/flora/sif icon = 'icons/obj/flora/sifflora.dmi' +/obj/structure/flora/sif/attack_hand(mob/user) + if (user.a_intent == I_HURT) + if(do_after(user, 5 SECONDS)) + user.visible_message("\The [user] digs up \the [src.name].", "You dig up \the [src.name].") + qdel(src) + else + user.visible_message("\The [user] pokes \the [src.name].", "You poke \the [src.name].") + /datum/category_item/catalogue/flora/subterranean_bulbs name = "Sivian Flora - Subterranean Bulbs" desc = "A plant which is native to Sif, it continues the trend of being a bioluminescent specimen. These plants \ @@ -341,3 +420,21 @@ /obj/structure/flora/sif/eyes/Initialize() icon_state = "[initial(icon_state)][rand(1,3)]" . = ..() + +/datum/category_item/catalogue/flora/mosstendrils + name = "Sivian Flora - Moss Stalks" + desc = "A plant native to Sif. The plant is most closely related to the common, dense moss found covering Sif's terrain. \ + It has evolved a method of camouflage utilizing white hairs on its dorsal sides to make it appear as a small mound of snow from \ + above. It has no known use, though it is a common furnishing in contemporary homes." + value = CATALOGUER_REWARD_TRIVIAL + +/obj/structure/flora/sif/tendrils + name = "stocky tendrils" + desc = "A 'plant' made up of hardened moss. It has tiny hairs that bunch together to look like snow." + icon_state = "grass" + randomize_size = TRUE + catalogue_data = list(/datum/category_item/catalogue/flora/mosstendrils) + +/obj/structure/flora/sif/tendrils/Initialize() + icon_state = "[initial(icon_state)][rand(1,3)]" + . = ..() diff --git a/code/game/objects/structures/flora/trees.dm b/code/game/objects/structures/flora/trees.dm index 9136db406f..c67348a94a 100644 --- a/code/game/objects/structures/flora/trees.dm +++ b/code/game/objects/structures/flora/trees.dm @@ -17,13 +17,30 @@ /obj/structure/flora/tree/Initialize() icon_state = choose_icon_state() + return ..() +/obj/structure/flora/tree/update_transform() + var/matrix/M = matrix() + M.Scale(icon_scale_x, icon_scale_y) + M.Translate(0, 16*(icon_scale_y-1)) + animate(src, transform = M, time = 10) + // Override this for special icons. /obj/structure/flora/tree/proc/choose_icon_state() return icon_state +/obj/structure/flora/tree/can_harvest(var/obj/item/I) + . = FALSE + if(!is_stump && harvest_tool && istype(I, harvest_tool) && harvest_loot && harvest_loot.len && harvest_count < max_harvests) + . = TRUE + return . + /obj/structure/flora/tree/attackby(var/obj/item/weapon/W, var/mob/living/user) + if(can_harvest(W)) + ..(W, user) + return + if(!istype(W)) return ..() @@ -57,8 +74,11 @@ /obj/structure/flora/tree/proc/hit_animation() var/init_px = pixel_x var/shake_dir = pick(-1, 1) - animate(src, transform=turn(matrix(), shake_animation_degrees * shake_dir), pixel_x=init_px + 2*shake_dir, time=1) - animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING) + var/matrix/M = matrix() + M.Scale(icon_scale_x, icon_scale_y) + M.Translate(0, 16*(icon_scale_y-1)) + animate(src, transform=turn(M, shake_animation_degrees * shake_dir), pixel_x=init_px + 2*shake_dir, time=1) + animate(transform=M, pixel_x=init_px, time=6, easing=ELASTIC_EASING) // Used when the tree gets hurt. /obj/structure/flora/tree/proc/adjust_health(var/amount, var/damage_wood = FALSE) @@ -247,13 +267,27 @@ base_state = "tree_sif" product = /obj/item/stack/material/log/sif catalogue_data = list(/datum/category_item/catalogue/flora/sif_tree) + randomize_size = TRUE + + harvest_tool = /obj/item/weapon/material/knife + max_harvests = 2 + min_harvests = -4 + harvest_loot = list( + /obj/item/weapon/reagent_containers/food/snacks/siffruit = 5 + ) + + var/light_shift = 0 + +/obj/structure/flora/tree/sif/choose_icon_state() + light_shift = rand(0, 5) + return "[base_state][light_shift]" /obj/structure/flora/tree/sif/Initialize() + . = ..() update_icon() - return ..() /obj/structure/flora/tree/sif/update_icon() - set_light(5, 1, "#33ccff") - var/image/glow = image(icon = 'icons/obj/flora/deadtrees.dmi', icon_state = "[base_state]_glow") + set_light(5 - light_shift, 1, "#33ccff") // 5 variants, missing bulbs. 5th has no bulbs, so no glow. + var/image/glow = image(icon = icon, icon_state = "[base_state][light_shift]_glow") glow.plane = PLANE_LIGHTING_ABOVE overlays = list(glow) diff --git a/code/game/objects/structures/ghost_pods/silicon_vr.dm b/code/game/objects/structures/ghost_pods/silicon_vr.dm new file mode 100644 index 0000000000..e9e707b664 --- /dev/null +++ b/code/game/objects/structures/ghost_pods/silicon_vr.dm @@ -0,0 +1,26 @@ +/obj/structure/ghost_pod/manual/lost_drone/dogborg + +/obj/structure/ghost_pod/manual/lost_drone/dogborg/create_occupant(var/mob/M) + var/response = alert(M, "What type of lost drone are you? Do note, that dogborgs may have experienced different type of corruption ((Potential for having vore-related laws))", "Drone Type", "Regular", "Dogborg") + if(!(response == "Dogborg")) // No response somehow or Regular + return ..() + else + density = FALSE + var/mob/living/silicon/robot/stray/randomlaws/R = new(get_turf(src)) + R.adjustBruteLoss(rand(5, 30)) + R.adjustFireLoss(rand(5, 10)) + if(M.mind) + M.mind.transfer_to(R) + // Put this text here before ckey change so that their laws are shown below it, since borg login() shows it. + to_chat(M, "You are a Stray Drone, discovered inside the wreckage of your previous home. \ + Something has reactivated you, with their intentions unknown to you, and yours unknown to them. They are a foreign entity, \ + however they did free you from your pod...") + to_chat(M, "Be sure to examine your currently loaded lawset closely. Remember, your \ + definiton of 'the station' is where your pod is, and unless your laws say otherwise, the entity that released you \ + from the pod is not a crewmember.") + R.ckey = M.ckey + visible_message("As \the [src] opens, the eyes of the robot flicker as it is activated.") + R.Namepick() + log_and_message_admins("successfully opened \a [src] and got a Stray Drone.") + used = TRUE + return TRUE \ No newline at end of file diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index a24f2868da..2648723385 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -38,7 +38,7 @@ if(!total_radiation) return - radiation_repository.radiate(src, total_radiation) + SSradiation.radiate(src, total_radiation) return total_radiation @@ -212,7 +212,7 @@ else return ..() -/obj/structure/girder/proc/take_damage(var/damage) +/obj/structure/girder/take_damage(var/damage) health -= damage if(health <= 0) dismantle() diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index c4b91fc73a..1057a5094c 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -281,3 +281,8 @@ return TRUE return FALSE +/obj/structure/grille/take_damage(var/damage) + health -= damage + spawn(1) healthcheck() + return 1 + diff --git a/code/game/objects/structures/handrail_vr.dm b/code/game/objects/structures/handrail_vr.dm new file mode 100644 index 0000000000..340d8dedd3 --- /dev/null +++ b/code/game/objects/structures/handrail_vr.dm @@ -0,0 +1,8 @@ +/obj/structure/handrail + name = "handrail" + icon = 'icons/obj/handrail_vr.dmi' + icon_state = "handrail" + desc = "A safety railing with buckles to secure yourself to when floor isn't stable enough." + density = 0 + anchored = 1 + can_buckle = 1 diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 2192b5690c..f5bebdf529 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -137,6 +137,13 @@ user.visible_message("[user] [attack_verb] at [src]!") return 1 +/obj/structure/inflatable/take_damage(var/damage) + health -= damage + if(health <= 0) + visible_message("The [src] deflates!") + spawn(1) puncture() + return 1 + /obj/item/inflatable/door/ name = "inflatable door" desc = "A folded membrane which rapidly expands into a simple door on activation." diff --git a/code/game/objects/structures/ledges.dm b/code/game/objects/structures/ledges.dm new file mode 100644 index 0000000000..1afa2efb01 --- /dev/null +++ b/code/game/objects/structures/ledges.dm @@ -0,0 +1,85 @@ +/obj/structure/ledge + name = "rock ledge" + desc = "An easily scaleable rocky ledge." + icon = 'icons/obj/ledges.dmi' + density = 1 + throwpass = 1 + climbable = 1 + anchored = 1 + var/solidledge = 1 + flags = ON_BORDER + layer = STAIRS_LAYER + icon_state = "ledge" + +/obj/structure/ledge_corner + icon_state = "ledge-corner" + flags = 0 + name = "rock ledge" + desc = "An easily scaleable rocky ledge." + icon = 'icons/obj/ledges.dmi' + density = 1 + throwpass = 1 + climbable = 1 + anchored = 1 + layer = STAIRS_LAYER + +/obj/structure/ledge/ledge_nub + desc = "Part of a rocky ledge." + icon_state = "ledge-nub" + density = 0 + solidledge = 0 + +/obj/structure/ledge/ledge_stairs + name = "rock stairs" + desc = "A colorful set of rocky stairs" + icon_state = "ledge-stairs" + density = 0 + solidledge = 0 + +/obj/structure/ledge/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(solidledge && get_dir(mover, target) == turn(dir, 180)) + return !density + return TRUE + +/obj/structure/ledge/CheckExit(atom/movable/O as mob|obj, target as turf) + if(istype(O) && O.checkpass(PASSTABLE)) + return 1 + if(solidledge && get_dir(O.loc, target) == dir) + return 0 + return 1 + +/obj/structure/ledge/do_climb(var/mob/living/user) + if(!can_climb(user)) + return + + usr.visible_message("[user] starts climbing onto \the [src]!") + climbers |= user + + if(!do_after(user,(issmall(user) ? 20 : 34))) + climbers -= user + return + + if(!can_climb(user, post_climb_check=1)) + climbers -= user + return + + if(get_turf(user) == get_turf(src)) + usr.forceMove(get_step(src, src.dir)) + else + usr.forceMove(get_turf(src)) + + usr.visible_message("[user] climbed over \the [src]!") + climbers -= user + +/obj/structure/ledge/can_climb(var/mob/living/user, post_climb_check=0) + if(!..()) + return 0 + + if(get_turf(user) == get_turf(src)) + var/obj/occupied = neighbor_turf_impassable() + if(occupied) + to_chat(user, "You can't climb there, there's \a [occupied] in the way.") + return 0 + return 1 \ No newline at end of file diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index c9731a0568..f74770664b 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -340,7 +340,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/weapon/stock_parts/subspace/transmitter, /obj/item/weapon/stock_parts/subspace/treatment, /obj/item/frame, - /obj/item/broken_device, + /obj/item/broken_device/random, /obj/item/borg/upgrade/restart, /obj/item/weapon/cell, /obj/item/weapon/cell/high, @@ -781,7 +781,8 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh // Todo: Better loot. /obj/structure/loot_pile/mecha/gygax/dark/adv icon_state = "darkgygax_adv-broken" - icon_scale = 1.5 + icon_scale_x = 1.5 + icon_scale_y = 1.5 pixel_y = 8 /obj/structure/loot_pile/mecha/gygax/medgax diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index f563a380cb..ff1826116a 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -1,8 +1,8 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 #define MUSICIAN_HEARCHECK_MINDELAY 4 -#define INSTRUMENT_MAX_LINE_LENGTH 300 -#define INSTRUMENT_MAX_LINE_NUMBER 50 +#define INSTRUMENT_MAX_LINE_LENGTH 50 +#define INSTRUMENT_MAX_LINE_NUMBER 300 /datum/song var/name = "Untitled" diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index 8f21f72781..d655581c92 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -8,6 +8,7 @@ layer = MOB_LAYER plane = MOB_PLANE explosion_resistance = 5 + var/can_pass_lying = TRUE var/list/mobs_can_pass = list( /mob/living/bot, /mob/living/simple_mob/slime/xenobio, @@ -18,9 +19,9 @@ /obj/structure/plasticflaps/attackby(obj/item/P, mob/user) if(P.is_wirecutter()) playsound(src, P.usesound, 50, 1) - user << "You start to cut the plastic flaps." + to_chat(user, "You start to cut the plastic flaps.") if(do_after(user, 10 * P.toolspeed)) - user << "You cut the plastic flaps." + to_chat(user, "You cut the plastic flaps.") var/obj/item/stack/material/plastic/A = new /obj/item/stack/material/plastic( src.loc ) A.amount = 4 qdel(src) @@ -36,12 +37,12 @@ if (istype(A, /obj/structure/bed) && B.has_buckled_mobs())//if it's a bed/chair and someone is buckled, it will not pass return 0 - if(istype(A, /obj/vehicle)) //no vehicles + if(istype(A, /obj/vehicle) || istype (A, /obj/mecha)) //no vehicles return 0 var/mob/living/M = A if(istype(M)) - if(M.lying) + if(M.lying && can_pass_lying) return ..() for(var/mob_type in mobs_can_pass) if(istype(A, mob_type)) @@ -63,5 +64,6 @@ /obj/structure/plasticflaps/mining //A specific type for mining that doesn't allow airflow because of them damn crates name = "airtight plastic flaps" - desc = "Heavy duty, airtight, plastic flaps." + desc = "Heavy duty, airtight, plastic flaps. Have extra safety installed, preventing passage of living beings." can_atmos_pass = ATMOS_PASS_NO + can_pass_lying = FALSE \ No newline at end of file diff --git a/code/game/objects/structures/props/blackbox.dm b/code/game/objects/structures/props/blackbox.dm index b6bae732f8..a28b82349f 100644 --- a/code/game/objects/structures/props/blackbox.dm +++ b/code/game/objects/structures/props/blackbox.dm @@ -105,4 +105,28 @@ **16/FEB/2562**
      Something chitters.
      End of transcript. - "} \ No newline at end of file + "} + +/obj/structure/prop/blackbox/xenofrigate + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/xenofrigate) + +/datum/category_item/catalogue/information/blackbox/xenofrigate + name = "Black Box Data - MBT-540" + desc = {" +
      + Begin Log + @$&@$& Human ##:##:##: Attention unidentified vessel, state your designation and intent.
      + !#@$&&^ Human ##:##:##: Commander I don't think they're going to stop.
      + @$&@$& Human ##:##:##: Unidentified vessel, you have until the count of three before we engage weapon-
      + !#@$&&^ Human ##:##:##: Commander! Think about what you're-
      + A repeating clicking, before silence.
      + End of first log.
      + **
      + Begin Log
      + #!#^@$& Skrell ##:##:##: Director, I think you should see this.
      + ^@$& Skrell ##:##:##: Yes? What is it?
      + #!#^@$& Skrell ##:##:##: Another one of those ships has appeared near th-462$^ ---n colonies. I would strongly advise pursuing it.
      + ^@$& Skrell ##:##:##: A wise decision. If it is damaged like the last one, we may be able to finally see what is - What?
      + A repeating ping, before silence.
      + End of second log. + "} diff --git a/code/game/objects/structures/props/puzzledoor.dm b/code/game/objects/structures/props/puzzledoor.dm index 70d6508202..f5ec2fe7cb 100644 --- a/code/game/objects/structures/props/puzzledoor.dm +++ b/code/game/objects/structures/props/puzzledoor.dm @@ -18,13 +18,17 @@ var/checkrange_mult = 1 /obj/machinery/door/blast/puzzle/proc/check_locks() + if(!locks || locks.len <= 0) // Puzzle doors with no locks will only listen to boring buttons. + return 0 + for(var/obj/structure/prop/lock/L in locks) if(!L.enabled) return 0 return 1 /obj/machinery/door/blast/puzzle/bullet_act(var/obj/item/projectile/Proj) - visible_message("\The [src] is completely unaffected by \the [Proj].") + if(!istype(Proj, /obj/item/projectile/test)) + visible_message("\The [src] is completely unaffected by \the [Proj].") qdel(Proj) //No piercing. No. /obj/machinery/door/blast/puzzle/ex_act(severity) diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index 9d96f0cc25..8027b5db5e 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -52,7 +52,7 @@ if(0.5 to 1.0) to_chat(user, "It has a few scrapes and dents.") -/obj/structure/railing/proc/take_damage(amount) +/obj/structure/railing/take_damage(amount) health -= amount if(health <= 0) visible_message("\The [src] breaks down!") diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 4b741ed648..60b94841ea 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -124,15 +124,22 @@ icon_state = material.door_icon_base /obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(istype(W,/obj/item/weapon/pickaxe)) var/obj/item/weapon/pickaxe/digTool = W - user << "You start digging the [name]." + visible_message("[user] starts digging [src]!") if(do_after(user,digTool.digspeed*hardness) && src) - user << "You finished digging." + visible_message("[user] finished digging [src]!") Dismantle() else if(istype(W,/obj/item/weapon)) //not sure, can't not just weapons get passed to this proc? - hardness -= W.force/100 - user << "You hit the [name] with your [W.name]!" + hardness -= W.force/10 + visible_message("[user] hits [src] with [W]!") + if(material == get_material_by_name("resin")) + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD))) + playsound(loc, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) CheckHardness() else if(istype(W,/obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W @@ -142,12 +149,33 @@ attack_hand(user) return +/obj/structure/simple_door/bullet_act(var/obj/item/projectile/Proj) + hardness -= Proj.force/10 + CheckHardness() + +/obj/structure/simple_door/take_damage(var/damage) + hardness -= damage/10 + CheckHardness() + +/obj/structure/simple_door/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + if(material == get_material_by_name("resin")) + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD))) + playsound(loc, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + hardness -= damage/10 + CheckHardness() + /obj/structure/simple_door/proc/CheckHardness() if(hardness <= 0) Dismantle(1) /obj/structure/simple_door/proc/Dismantle(devastated = 0) material.place_dismantled_product(get_turf(src)) + visible_message("The [src] is destroyed!") qdel(src) /obj/structure/simple_door/ex_act(severity = 1) @@ -168,7 +196,7 @@ /obj/structure/simple_door/process() if(!material.radioactivity) return - radiation_repository.radiate(src, round(material.radioactivity/3)) + SSradiation.radiate(src, round(material.radioactivity/3)) /obj/structure/simple_door/iron/New(var/newloc,var/material_name) ..(newloc, "iron") diff --git a/code/game/objects/structures/stasis_cage.dm b/code/game/objects/structures/stasis_cage.dm index 560f64251e..f4791f37f2 100644 --- a/code/game/objects/structures/stasis_cage.dm +++ b/code/game/objects/structures/stasis_cage.dm @@ -1,7 +1,7 @@ /obj/structure/stasis_cage name = "stasis cage" desc = "A high-tech animal cage, designed to keep contained fauna docile and safe." - icon = 'icons/obj/storage.dmi' + icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit icon_state = "critteropen" density = 1 @@ -64,4 +64,4 @@ usr.visible_message("[usr] has stuffed \the [src] into \the [over_object].", "You have stuffed \the [src] into \the [over_object].") over_object.contain(src) else - return ..() \ No newline at end of file + return ..() diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm new file mode 100644 index 0000000000..e82ef467bb --- /dev/null +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm @@ -0,0 +1,20 @@ +/obj/structure/bed/chair/sofa + name = "sofa" + desc = "A padded, comfy sofa. Great for lazing on." + base_icon = "sofamiddle" + +/obj/structure/bed/chair/sofa/left + base_icon = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + base_icon = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + base_icon = "sofacorner" + +/obj/structure/bed/chair/sofa/corner/update_layer() + if(src.dir == NORTH || src.dir == WEST) + plane = MOB_PLANE + layer = MOB_LAYER + 0.1 + else + reset_plane_and_layer() \ No newline at end of file diff --git a/code/game/objects/structures/trash_pile.dm b/code/game/objects/structures/trash_pile.dm index 716de5a39e..bc06bb0dbd 100644 --- a/code/game/objects/structures/trash_pile.dm +++ b/code/game/objects/structures/trash_pile.dm @@ -20,6 +20,7 @@ var/global/list/unique_gamma = list( /obj/item/device/perfect_tele, /obj/item/weapon/bluespace_harpoon, + /obj/item/clothing/glasses/thermal/syndi, /obj/item/weapon/gun/energy/netgun, /obj/item/weapon/gun/projectile/pirate, /obj/item/clothing/accessory/permit/gun, @@ -131,7 +132,7 @@ prob(5);/obj/item/weapon/storage/backpack/satchel/norm, prob(5);/obj/item/weapon/storage/box, // prob(5);/obj/random/cigarettes, - prob(4);/obj/item/broken_device, + prob(4);/obj/item/broken_device/random, prob(4);/obj/item/clothing/head/hardhat, prob(4);/obj/item/clothing/mask/breath, prob(4);/obj/item/clothing/shoes/black, @@ -221,13 +222,13 @@ prob(4);/obj/item/weapon/storage/pill_bottle/happy, prob(4);/obj/item/weapon/storage/pill_bottle/zoom, prob(4);/obj/item/weapon/gun/energy/sizegun, - prob(3);/obj/item/weapon/implanter/sizecontrol, prob(3);/obj/item/weapon/material/butterfly, prob(3);/obj/item/weapon/material/butterfly/switchblade, prob(3);/obj/item/clothing/gloves/knuckledusters, prob(3);/obj/item/weapon/reagent_containers/syringe/drugs, + prob(2);/obj/item/weapon/implanter/sizecontrol, prob(2);/obj/item/weapon/handcuffs/fuzzy, - // prob(2);/obj/item/weapon/legcuffs, + prob(2);/obj/item/weapon/handcuffs/legcuffs/fuzzy, prob(2);/obj/item/weapon/storage/box/syndie_kit/spy, prob(2);/obj/item/weapon/grenade/anti_photon, prob(1);/obj/item/clothing/suit/storage/vest/heavy/merc, @@ -239,6 +240,8 @@ prob(1);/obj/item/weapon/cell/hyper/empty, prob(1);/obj/item/weapon/disk/nifsoft/compliance, prob(1);/obj/item/weapon/material/knife/tacknife, + prob(1);/obj/item/weapon/storage/box/survival/space, + prob(1);/obj/item/weapon/storage/secure/briefcase/trashmoney, prob(1);/obj/item/weapon/reagent_containers/syringe/steroid) var/obj/item/I = new path() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index cc3bcf1648..b2867d5ead 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -47,7 +47,7 @@ else to_chat(user, "There is a thick layer of silicate covering it.") -/obj/structure/window/proc/take_damage(var/damage = 0, var/sound_effect = 1) +/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1) var/initialhealth = health if(silicate) diff --git a/code/game/sound.dm b/code/game/sound.dm index b1f0c32f66..cd3b140bd9 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -137,6 +137,46 @@ if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + //VORESTATION EDIT - vore sounds for better performance + if ("hunger_sounds") soundin = pick('sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg','sound/vore/growl5.ogg') + + if("classic_digestion_sounds") soundin = pick( + 'sound/vore/digest1.ogg','sound/vore/digest2.ogg','sound/vore/digest3.ogg','sound/vore/digest4.ogg', + 'sound/vore/digest5.ogg','sound/vore/digest6.ogg','sound/vore/digest7.ogg','sound/vore/digest8.ogg', + 'sound/vore/digest9.ogg','sound/vore/digest10.ogg','sound/vore/digest11.ogg','sound/vore/digest12.ogg') + if("classic_death_sounds") soundin = pick( + 'sound/vore/death1.ogg','sound/vore/death2.ogg','sound/vore/death3.ogg','sound/vore/death4.ogg','sound/vore/death5.ogg', + 'sound/vore/death6.ogg','sound/vore/death7.ogg','sound/vore/death8.ogg','sound/vore/death9.ogg','sound/vore/death10.ogg') + if("classic_struggle_sounds") soundin = pick('sound/vore/squish1.ogg','sound/vore/squish2.ogg','sound/vore/squish3.ogg','sound/vore/squish4.ogg') + + if("fancy_prey_struggle") soundin = pick( + 'sound/vore/sunesound/prey/struggle_01.ogg','sound/vore/sunesound/prey/struggle_02.ogg','sound/vore/sunesound/prey/struggle_03.ogg', + 'sound/vore/sunesound/prey/struggle_04.ogg','sound/vore/sunesound/prey/struggle_05.ogg') + if("fancy_digest_pred") soundin = pick( + 'sound/vore/sunesound/pred/digest_01.ogg','sound/vore/sunesound/pred/digest_02.ogg','sound/vore/sunesound/pred/digest_03.ogg', + 'sound/vore/sunesound/pred/digest_04.ogg','sound/vore/sunesound/pred/digest_05.ogg','sound/vore/sunesound/pred/digest_06.ogg', + 'sound/vore/sunesound/pred/digest_07.ogg','sound/vore/sunesound/pred/digest_08.ogg','sound/vore/sunesound/pred/digest_09.ogg', + 'sound/vore/sunesound/pred/digest_10.ogg','sound/vore/sunesound/pred/digest_11.ogg','sound/vore/sunesound/pred/digest_12.ogg', + 'sound/vore/sunesound/pred/digest_13.ogg','sound/vore/sunesound/pred/digest_14.ogg','sound/vore/sunesound/pred/digest_15.ogg', + 'sound/vore/sunesound/pred/digest_16.ogg','sound/vore/sunesound/pred/digest_17.ogg','sound/vore/sunesound/pred/digest_18.ogg') + if("fancy_death_pred") soundin = pick( + 'sound/vore/sunesound/pred/death_01.ogg','sound/vore/sunesound/pred/death_02.ogg','sound/vore/sunesound/pred/death_03.ogg', + 'sound/vore/sunesound/pred/death_04.ogg','sound/vore/sunesound/pred/death_05.ogg','sound/vore/sunesound/pred/death_06.ogg', + 'sound/vore/sunesound/pred/death_07.ogg','sound/vore/sunesound/pred/death_08.ogg','sound/vore/sunesound/pred/death_09.ogg', + 'sound/vore/sunesound/pred/death_10.ogg') + if("fancy_digest_prey") soundin = pick( + 'sound/vore/sunesound/prey/digest_01.ogg','sound/vore/sunesound/prey/digest_02.ogg','sound/vore/sunesound/prey/digest_03.ogg', + 'sound/vore/sunesound/prey/digest_04.ogg','sound/vore/sunesound/prey/digest_05.ogg','sound/vore/sunesound/prey/digest_06.ogg', + 'sound/vore/sunesound/prey/digest_07.ogg','sound/vore/sunesound/prey/digest_08.ogg','sound/vore/sunesound/prey/digest_09.ogg', + 'sound/vore/sunesound/prey/digest_10.ogg','sound/vore/sunesound/prey/digest_11.ogg','sound/vore/sunesound/prey/digest_12.ogg', + 'sound/vore/sunesound/prey/digest_13.ogg','sound/vore/sunesound/prey/digest_14.ogg','sound/vore/sunesound/prey/digest_15.ogg', + 'sound/vore/sunesound/prey/digest_16.ogg','sound/vore/sunesound/prey/digest_17.ogg','sound/vore/sunesound/prey/digest_18.ogg') + if("fancy_death_prey") soundin = pick( + 'sound/vore/sunesound/prey/death_01.ogg','sound/vore/sunesound/prey/death_02.ogg','sound/vore/sunesound/prey/death_03.ogg', + 'sound/vore/sunesound/prey/death_04.ogg','sound/vore/sunesound/prey/death_05.ogg','sound/vore/sunesound/prey/death_06.ogg', + 'sound/vore/sunesound/prey/death_07.ogg','sound/vore/sunesound/prey/death_08.ogg','sound/vore/sunesound/prey/death_09.ogg', + 'sound/vore/sunesound/prey/death_10.ogg') + //END VORESTATION EDIT return soundin //Are these even used? diff --git a/code/game/turfs/flooring/flooring_decals.dm b/code/game/turfs/flooring/flooring_decals.dm index f69d676ccd..d6e36b30e2 100644 --- a/code/game/turfs/flooring/flooring_decals.dm +++ b/code/game/turfs/flooring/flooring_decals.dm @@ -469,8 +469,8 @@ var/list/floor_decals = list() /obj/effect/floor_decal/corner/grey/bordercorner icon_state = "bordercolorcorner" -/obj/effect/floor_decal/corner/grey/bordercorner - icon_state = "bordercolorcorner" +/obj/effect/floor_decal/corner/grey/bordercorner2 + icon_state = "bordercolorcorner2" /obj/effect/floor_decal/corner/grey/borderfull icon_state = "bordercolorfull" diff --git a/code/game/turfs/flooring/shuttle_vr.dm b/code/game/turfs/flooring/shuttle_vr.dm index f7f1b6e497..65b586b931 100644 --- a/code/game/turfs/flooring/shuttle_vr.dm +++ b/code/game/turfs/flooring/shuttle_vr.dm @@ -8,3 +8,33 @@ oxygen = 0 nitrogen = 0 temperature = TCMB + +/turf/simulated/shuttle/floor/white/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/shuttle/floor/yellow/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/shuttle/floor/purple/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/shuttle/floor/red/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/shuttle/floor/darkred/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/shuttle/floor/black/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 1c74b467e2..6bf7cf38b4 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -13,6 +13,8 @@ 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 var/can_dirty = TRUE // If false, tile never gets dirty + var/can_start_dirty = TRUE // If false, cannot start dirty roundstart + var/dirty_prob = 2 // Chance of being dirty roundstart var/dirt = 0 // This is not great. diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index b615c0cc42..31bfd62684 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -43,6 +43,10 @@ set_flooring(get_flooring_data(floortype)) else footstep_sounds = base_footstep_sounds + if(can_dirty && can_start_dirty) + if(prob(dirty_prob)) + dirt += rand(50,100) + update_dirt() //5% chance to start with dirt on a floor tile- give the janitor something to do /turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring) make_plating(defer_icon_update = 1) diff --git a/code/game/turfs/simulated/outdoors/grass.dm b/code/game/turfs/simulated/outdoors/grass.dm index 67aa054b46..e557fb16da 100644 --- a/code/game/turfs/simulated/outdoors/grass.dm +++ b/code/game/turfs/simulated/outdoors/grass.dm @@ -29,11 +29,12 @@ var/list/grass_types = list( name = "growth" icon_state = "grass_sif" edge_blending_priority = 4 - grass_chance = 0 + grass_chance = 5 var/tree_chance = 2 grass_types = list( - /obj/structure/flora/sif/eyes + /obj/structure/flora/sif/eyes = 1, + /obj/structure/flora/sif/tendrils = 10 ) catalogue_data = list(/datum/category_item/catalogue/flora/sif_grass) @@ -50,7 +51,7 @@ var/list/grass_types = list( //edge_blending_priority++ if(grass_chance && prob(grass_chance) && !check_density()) - var/grass_type = pick(grass_types) + var/grass_type = pickweight(grass_types) new grass_type(src) . = ..() diff --git a/code/game/turfs/simulated/outdoors/outdoors_attackby.dm b/code/game/turfs/simulated/outdoors/outdoors_attackby.dm new file mode 100644 index 0000000000..e38f601b8b --- /dev/null +++ b/code/game/turfs/simulated/outdoors/outdoors_attackby.dm @@ -0,0 +1,12 @@ +// this code here enables people to dig up worms from certain tiles. + +/turf/simulated/floor/outdoors/grass/attackby(obj/item/weapon/S as obj, mob/user as mob) + if(istype(S, /obj/item/weapon/shovel)) + to_chat(user, "You begin to dig in \the [src] with your [S].") + if(do_after(user, 4 SECONDS * S.toolspeed)) + to_chat(user, "\The [src] has been dug up, a worm pops from the ground.") + new /obj/item/weapon/reagent_containers/food/snacks/worm(src) + else + to_chat(user, "You decide to not finish digging in \the [src].") + else + ..() \ No newline at end of file diff --git a/code/game/turfs/simulated/outdoors/snow.dm b/code/game/turfs/simulated/outdoors/snow.dm index 0f9be4ddbf..cccc028fa3 100644 --- a/code/game/turfs/simulated/outdoors/snow.dm +++ b/code/game/turfs/simulated/outdoors/snow.dm @@ -58,3 +58,10 @@ to_chat(M, "You slide across the ice!") M.SetStunned(1) step(M,M.dir) + +// Ice that is used for, say, areas floating on water or similar. +/turf/simulated/floor/outdoors/shelfice + name = "ice" + icon_state = "ice" + desc = "Looks slippery." + movement_cost = 4 diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index bd6adfe32f..8599def4a5 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -7,7 +7,7 @@ if(can_open == WALL_OPENING) return - radiation_repository.resistance_cache.Remove(src) + SSradiation.resistance_cache.Remove(src) if(density) can_open = WALL_OPENING diff --git a/code/game/turfs/simulated/wall_icon.dm b/code/game/turfs/simulated/wall_icon.dm index b8a0980de4..f277ea79f5 100644 --- a/code/game/turfs/simulated/wall_icon.dm +++ b/code/game/turfs/simulated/wall_icon.dm @@ -26,7 +26,7 @@ else if(material.opacity < 0.5 && opacity) set_light(0) - radiation_repository.resistance_cache.Remove(src) + SSradiation.resistance_cache.Remove(src) update_connections(1) update_icon() diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index cb1a0f631a..bf5f711b20 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -153,7 +153,7 @@ visible_message("\The [src] spontaneously combusts!.") //!!OH SHIT!! return -/turf/simulated/wall/proc/take_damage(dam) +/turf/simulated/wall/take_damage(dam) if(dam) damage = max(0, damage + dam) update_damage() @@ -274,7 +274,7 @@ if(!total_radiation) return - radiation_repository.radiate(src, total_radiation) + SSradiation.radiate(src, total_radiation) return total_radiation /turf/simulated/wall/proc/burn(temperature) diff --git a/code/game/turfs/simulated/water.dm b/code/game/turfs/simulated/water.dm index 33901a8b2b..25671feca2 100644 --- a/code/game/turfs/simulated/water.dm +++ b/code/game/turfs/simulated/water.dm @@ -19,6 +19,7 @@ /turf/simulated/floor/water/Initialize() . = ..() update_icon() + handle_fish() /turf/simulated/floor/water/update_icon() ..() // To get the edges. diff --git a/code/game/turfs/simulated/water_vr.dm b/code/game/turfs/simulated/water_vr.dm new file mode 100644 index 0000000000..ea16037cf7 --- /dev/null +++ b/code/game/turfs/simulated/water_vr.dm @@ -0,0 +1,5 @@ +/turf/simulated/floor/water/indoors //because it's nice to be able to use these indoors without having a blizzard ignore walls and areas. + outdoors = FALSE + +/turf/simulated/floor/water/deep/indoors + outdoors = FALSE \ No newline at end of file diff --git a/code/game/turfs/simulated_vr.dm b/code/game/turfs/simulated_vr.dm new file mode 100644 index 0000000000..6c7325305f --- /dev/null +++ b/code/game/turfs/simulated_vr.dm @@ -0,0 +1,5 @@ +/turf/simulated + can_start_dirty = FALSE // We have enough premapped dirt where needed + +/turf/simulated/floor/plating + can_start_dirty = TRUE // But let maints and decrepit areas have some randomness \ No newline at end of file diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 0c97ec387e..cb3496a96b 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -28,7 +28,7 @@ if(N == /turf/space) var/turf/below = GetBelow(src) - if(istype(below) && (air_master.has_valid_zone(below) || air_master.has_valid_zone(src))) + if(istype(below) && (air_master.has_valid_zone(below) || air_master.has_valid_zone(src)) && (!istype(below, /turf/unsimulated/wall) && !istype(below, /turf/simulated/sky))) // VOREStation Edit: Weird open space N = /turf/simulated/open var/obj/fire/old_fire = fire diff --git a/code/game/turfs/unsimulated/planetary_vr.dm b/code/game/turfs/unsimulated/planetary_vr.dm new file mode 100644 index 0000000000..5cf174cf3d --- /dev/null +++ b/code/game/turfs/unsimulated/planetary_vr.dm @@ -0,0 +1,30 @@ +//Atmosphere properties +#define VIRGO3B_ONE_ATMOSPHERE 82.4 //kPa +#define VIRGO3B_AVG_TEMP 234 //kelvin + +#define VIRGO3B_PER_N2 0.16 //percent +#define VIRGO3B_PER_O2 0.00 +#define VIRGO3B_PER_N2O 0.00 //Currently no capacity to 'start' a turf with this. See turf.dm +#define VIRGO3B_PER_CO2 0.12 +#define VIRGO3B_PER_PHORON 0.72 + +//Math only beyond this point +#define VIRGO3B_MOL_PER_TURF (VIRGO3B_ONE_ATMOSPHERE*CELL_VOLUME/(VIRGO3B_AVG_TEMP*R_IDEAL_GAS_EQUATION)) +#define VIRGO3B_MOL_N2 (VIRGO3B_MOL_PER_TURF * VIRGO3B_PER_N2) +#define VIRGO3B_MOL_O2 (VIRGO3B_MOL_PER_TURF * VIRGO3B_PER_O2) +#define VIRGO3B_MOL_N2O (VIRGO3B_MOL_PER_TURF * VIRGO3B_PER_N2O) +#define VIRGO3B_MOL_CO2 (VIRGO3B_MOL_PER_TURF * VIRGO3B_PER_CO2) +#define VIRGO3B_MOL_PHORON (VIRGO3B_MOL_PER_TURF * VIRGO3B_PER_PHORON) + +//Turfmakers +#define VIRGO3B_SET_ATMOS nitrogen=VIRGO3B_MOL_N2;oxygen=VIRGO3B_MOL_O2;carbon_dioxide=VIRGO3B_MOL_CO2;phoron=VIRGO3B_MOL_PHORON;temperature=VIRGO3B_AVG_TEMP +#define VIRGO3B_TURF_CREATE(x) x/virgo3b/nitrogen=VIRGO3B_MOL_N2;x/virgo3b/oxygen=VIRGO3B_MOL_O2;x/virgo3b/carbon_dioxide=VIRGO3B_MOL_CO2;x/virgo3b/phoron=VIRGO3B_MOL_PHORON;x/virgo3b/temperature=VIRGO3B_AVG_TEMP;x/virgo3b/outdoors=TRUE;x/virgo3b/update_graphic(list/graphic_add = null, list/graphic_remove = null) return 0 +#define VIRGO3B_TURF_CREATE_UN(x) x/virgo3b/nitrogen=VIRGO3B_MOL_N2;x/virgo3b/oxygen=VIRGO3B_MOL_O2;x/virgo3b/carbon_dioxide=VIRGO3B_MOL_CO2;x/virgo3b/phoron=VIRGO3B_MOL_PHORON;x/virgo3b/temperature=VIRGO3B_AVG_TEMP + +// This is a wall you surround the area of your "planet" with, that makes the atmosphere inside stay within bounds, even if canisters +// are opened or other strange things occur. +/turf/unsimulated/wall/planetary/virgo3b + name = "facility wall" + desc = "An eight-meter tall carbyne wall. For when the wildlife on your planet is mostly militant megacorps." + alpha = 0xFF + VIRGO3B_SET_ATMOS diff --git a/code/game/turfs/unsimulated/sky_vr.dm b/code/game/turfs/unsimulated/sky_vr.dm index 8ba1cf0cb5..c71fa6470a 100644 --- a/code/game/turfs/unsimulated/sky_vr.dm +++ b/code/game/turfs/unsimulated/sky_vr.dm @@ -1,67 +1,73 @@ -/////////////////// -// Generic skyfall turf -// Really only works well if the map doesn't have 'indoor' areas otherwise they can fall into one. -// TODO: Fix that. -/turf/unsimulated/floor/sky - name = "the sky" - desc = "It's the sky! Be careful!" - icon = 'icons/turf/floors.dmi' - icon_state = "sky_slow" - dir = SOUTH - initialized = FALSE - var/does_skyfall = TRUE - var/list/skyfall_levels - -/turf/unsimulated/floor/sky/Initialize() - . = ..() - if(does_skyfall && !LAZYLEN(skyfall_levels)) - error("[x],[y],[z], [get_area(src)] doesn't have skyfall_levels defined! Can't skyfall!") - if(locate(/turf/simulated) in orange(src,1)) - set_light(2, 2, color) - -/turf/unsimulated/floor/sky/Entered(atom/movable/AM,atom/oldloc) - . = ..() - if(!does_skyfall) - return //We don't do that - if(isobserver(AM)) - return //Don't ghostport, very annoying - if(AM.throwing) - return //Being thrown over, not fallen yet - - var/mob/living/L - if(isliving(AM)) - L = AM - if(L.is_floating) - return //Flyers/nograv can ignore it - - do_fall(AM) - -/turf/unsimulated/floor/sky/hitby(var/atom/movable/AM, var/speed) - . = ..() - - if(!does_skyfall) - return //We don't do that - - do_fall(AM) - -/turf/unsimulated/floor/sky/proc/do_fall(atom/movable/AM) - //Bye - var/attempts = 100 - var/turf/simulated/T - while(attempts && !T) - var/turf/simulated/candidate = locate(rand(5,world.maxx-5),rand(5,world.maxy-5),pick(skyfall_levels)) - if(candidate.density) - attempts-- - continue - - T = candidate - break - - if(!T) - return - - AM.forceMove(T) - if(isliving(AM)) - var/mob/living/L = AM - message_admins("\The [AM] fell out of the sky.") - L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. +/////////////////// +// Generic skyfall turf +// Really only works well if the map doesn't have 'indoor' areas otherwise they can fall into one. +// TODO: Fix that. +/turf/unsimulated/floor/sky + name = "the sky" + desc = "It's the sky! Be careful!" + icon = 'icons/turf/floors.dmi' + icon_state = "sky_slow" + dir = SOUTH + initialized = FALSE + var/does_skyfall = TRUE + var/list/skyfall_levels + +/turf/unsimulated/floor/sky/Initialize() + . = ..() + if(does_skyfall && !LAZYLEN(skyfall_levels)) + error("[x],[y],[z], [get_area(src)] doesn't have skyfall_levels defined! Can't skyfall!") + if(locate(/turf/simulated) in orange(src,1)) + set_light(2, 2, color) + +/turf/unsimulated/floor/sky/Entered(atom/movable/AM,atom/oldloc) + . = ..() + if(!does_skyfall) + return //We don't do that + if(isobserver(AM)) + return //Don't ghostport, very annoying + if(AM.throwing) + return //Being thrown over, not fallen yet + if(!(AM.can_fall())) + return // Phased shifted kin should not fall + if(istype(AM, /obj/item/projectile)) + return // pewpew should not fall out of the sky. pew. + if(istype(AM, /obj/effect/projectile)) + return // ...neither should the effects be falling + + var/mob/living/L + if(isliving(AM)) + L = AM + if(L.is_floating) + return //Flyers/nograv can ignore it + + do_fall(AM) + +/turf/unsimulated/floor/sky/hitby(var/atom/movable/AM, var/speed) + . = ..() + + if(!does_skyfall) + return //We don't do that + + do_fall(AM) + +/turf/unsimulated/floor/sky/proc/do_fall(atom/movable/AM) + //Bye + var/attempts = 100 + var/turf/simulated/T + while(attempts && !T) + var/turf/simulated/candidate = locate(rand(5,world.maxx-5),rand(5,world.maxy-5),pick(skyfall_levels)) + if(candidate.density) + attempts-- + continue + + T = candidate + break + + if(!T) + return + + AM.forceMove(T) + if(isliving(AM)) + var/mob/living/L = AM + message_admins("\The [AM] fell out of the sky.") + L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. diff --git a/code/game/turfs/unsimulated/walls.dm b/code/game/turfs/unsimulated/walls.dm index 12d4d3fadc..6f13cdb54b 100644 --- a/code/game/turfs/unsimulated/walls.dm +++ b/code/game/turfs/unsimulated/walls.dm @@ -4,6 +4,7 @@ icon_state = "riveted" opacity = 1 density = 1 + blocks_air = TRUE /turf/unsimulated/wall/fakeglass name = "window" diff --git a/code/game/verbs/ignore.dm b/code/game/verbs/ignore.dm index ea1ad6a0b7..686a9ed5af 100644 --- a/code/game/verbs/ignore.dm +++ b/code/game/verbs/ignore.dm @@ -15,7 +15,7 @@ return prefs.ignored_players |= key_to_ignore - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) usr << "Now ignoring [key_to_ignore]." /client/verb/unignore(key_to_unignore as text) @@ -31,7 +31,7 @@ usr << "[key_to_unignore] isn't being ignored." return prefs.ignored_players -= key_to_unignore - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) usr << "Reverted ignore on [key_to_unignore]." /mob/proc/is_key_ignored(var/key_to_check) diff --git a/code/game/world.dm b/code/game/world.dm index b9bdab1ca2..b611c884b0 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -159,6 +159,7 @@ var/world_topic_spam_protect_time = world.timeofday "med" = medical_positions, "sci" = science_positions, "car" = cargo_positions, + "pla" = planet_positions, //VOREStation Edit, "civ" = civilian_positions, "bot" = nonhuman_positions ) diff --git a/code/global.dm b/code/global.dm index ce0d041d26..e9bcbb49f4 100644 --- a/code/global.dm +++ b/code/global.dm @@ -53,7 +53,7 @@ var/list/lastsignalers = list() // Keeps last 100 signals here in format: "[src] var/list/lawchanges = list() // Stores who uploaded laws to which silicon-based lifeform, and what the law was. var/list/reg_dna = list() -var/mouse_respawn_time = 5 // Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes. +var/mouse_respawn_time = 2.5 // Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes. Vorestation Edit - Changed to 2.5 minutes, half of 5, in accordance with mouse nerfs and realignment. var/list/monkeystart = list() var/list/wizardstart = list() @@ -173,7 +173,7 @@ var/max_explosion_range = 14 // Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it. var/global/obj/item/device/radio/intercom/omni/global_announcer = new /obj/item/device/radio/intercom/omni(null) -var/list/station_departments = list("Command", "Medical", "Engineering", "Science", "Security", "Cargo", "Civilian") +var/list/station_departments = list("Command", "Medical", "Engineering", "Science", "Security", "Cargo", "Exploration", "Civilian") //VOREStation Edit //Icons for in-game HUD glasses. Why don't we just share these a little bit? var/static/icon/ingame_hud = icon('icons/mob/hud.dmi') diff --git a/code/global_vr.dm b/code/global_vr.dm index 9506dd62d3..0f3212ff7a 100644 --- a/code/global_vr.dm +++ b/code/global_vr.dm @@ -6,6 +6,12 @@ robot_module_types += "Pupdozer" return 1 +var/list/shell_module_types = list( + "Standard", "Service", "Clerical" +) + +var/list/eventdestinations = list() // List of scatter landmarks for VOREStation event portals + var/global/list/acceptable_fruit_types= list( "ambrosia", "apple", @@ -13,22 +19,35 @@ var/global/list/acceptable_fruit_types= list( "berries", "cabbage", "carrot", + "celery", "cherry", "chili", + "cocoa", + "corn", + "durian", "eggplant", "grapes", "greengrapes", + "harebells", + "lavender", "lemon", + "lettuce", "lime", "onion", "orange", "peanut", + "poppies", "potato", "pumpkin", "rice", + "rose", + "rhubarb", "soybean", + "spineapple", "sugarcane", + "sunflowers", "tomato", + "vanilla", "watermelon", "wheat", "whitebeet") \ No newline at end of file diff --git a/code/modules/admin/admin_attack_log.dm b/code/modules/admin/admin_attack_log.dm index f04bdf3b61..c1f5c67464 100644 --- a/code/modules/admin/admin_attack_log.dm +++ b/code/modules/admin/admin_attack_log.dm @@ -1,6 +1,7 @@ /mob/var/lastattacker = null /mob/var/lastattacked = null /mob/var/attack_log = list( ) +/mob/var/dialogue_log = list( ) proc/log_and_message_admins(var/message as text, var/mob/user = usr) log_admin(user ? "[key_name(user)] [message]" : "EVENT [message]") diff --git a/code/modules/admin/admin_tools.dm b/code/modules/admin/admin_tools.dm new file mode 100644 index 0000000000..376aa2da44 --- /dev/null +++ b/code/modules/admin/admin_tools.dm @@ -0,0 +1,62 @@ +/client/proc/cmd_admin_check_player_logs(mob/living/M as mob in mob_list) + set category = "Admin" + set name = "Check Player Attack Logs" + set desc = "Check a player's attack logs." + +//Views specific attack logs belonging to one player. + var/dat = "[M]'s Attack Log:
      " + dat += "Viewing attack logs of [M] - (Played by ([key_name(M)]).
      " + if(M.mind) + dat += "Current Antag?: [(M.mind.special_role)?"Yes":"No"]
      " + dat += "
      Note: This is arranged from earliest to latest.

      " + + + if(!isemptylist(M.attack_log)) + dat += "
      " + for(var/l in M.attack_log) + dat += "[l]
      " + + dat += "
      " + + else + dat += "No attack logs found for [M]." + + var/datum/browser/popup = new(usr, "admin_attack_log", "[src]", 650, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(usr, "admin_attack_log") + + feedback_add_details("admin_verb","PL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_check_dialogue_logs(mob/living/M as mob in mob_list) + set category = "Admin" + set name = "Check Player Dialogue Logs" + set desc = "Check a player's dialogue logs." + +//Views specific dialogue logs belonging to one player. + var/dat = "[M]'s Dialogue Log:
      " + dat += "Viewing say and emote logs of [M] - (Played by ([key_name(M)]).
      " + if(M.mind) + dat += "Current Antag?: [(M.mind.special_role)?"Yes":"No"]
      " + dat += "
      Note: This is arranged from earliest to latest.

      " + + if(!isemptylist(M.dialogue_log)) + dat += "
      " + + for(var/d in M.dialogue_log) + dat += "[d]
      " + + dat += "
      " + else + dat += "No dialogue logs found for [M]." + var/datum/browser/popup = new(usr, "admin_dialogue_log", "[src]", 650, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(usr, "admin_dialogue_log") + + + feedback_add_details("admin_verb","PDL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 02d3c67186..9f8e6271f3 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -38,6 +38,8 @@ var/list/admin_verbs_admin = list( /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/cmd_admin_check_player_logs, //checks a player's attack logs, + /client/proc/cmd_admin_check_dialogue_logs, //checks a player's dialogue logs, /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/getserverlog, //allows us to fetch server logs (diary) for other days, @@ -148,6 +150,7 @@ var/list/admin_verbs_spawn = list( /datum/admins/proc/spawn_plant, /datum/admins/proc/spawn_atom, //allows us to spawn instances, /client/proc/respawn_character, + /client/proc/spawn_character_mob, //VOREStation Add, /client/proc/virus2_editor, /client/proc/spawn_chemdisp_cartridge, /client/proc/map_template_load, @@ -266,6 +269,8 @@ var/list/admin_verbs_hideable = list( /datum/admins/proc/view_atk_log, /client/proc/cmd_admin_subtle_message, /client/proc/cmd_admin_check_contents, + /client/proc/cmd_admin_check_player_logs, + /client/proc/cmd_admin_check_dialogue_logs, /datum/admins/proc/access_news_network, /client/proc/admin_call_shuttle, /client/proc/admin_cancel_shuttle, @@ -587,7 +592,7 @@ var/list/admin_verbs_event_manager = list( prefs.ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color else if(response == "Reset to default") prefs.ooccolor = initial(prefs.ooccolor) - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -1064,7 +1069,7 @@ var/list/admin_verbs_event_manager = list( if(alert("Are you sure you want to tell them to man up?","Confirmation","Deal with it","No")=="No") return T << "Man up and deal with it." - T << "Move on." + T << "Move along." log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.", 1) @@ -1077,7 +1082,7 @@ var/list/admin_verbs_event_manager = list( if(alert("Are you sure you want to tell the whole server up?","Confirmation","Deal with it","No")=="No") return for (var/mob/T as mob in mob_list) - T << "
      Man up.
      Deal with it.

      Move on.

      " + T << "
      Man up.
      Deal with it.

      Move along.

      " T << 'sound/voice/ManUp1.ogg' log_admin("[key_name(usr)] told everyone to man up and deal with it.") diff --git a/code/modules/admin/secrets/admin_secrets/admin_logs.dm b/code/modules/admin/secrets/admin_secrets/admin_logs.dm index d2664f1e2b..b578eb046f 100644 --- a/code/modules/admin/secrets/admin_secrets/admin_logs.dm +++ b/code/modules/admin/secrets/admin_secrets/admin_logs.dm @@ -10,4 +10,35 @@ dat += "
    11. [l]
    12. " if(!admin_log.len) dat += "No-one has done anything this round!" - user << browse(dat, "window=admin_log") + + var/datum/browser/popup = new(user, "adminlogs", "[src]", 550, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(user, "adminlogs") + + +/datum/admin_secret_item/admin_secret/round_logs + name = "Round Dialogue Logs" + +/datum/admin_secret_item/admin_secret/round_logs/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Dialogue Log
      " + + dat += "
      " + + for(var/l in GLOB.round_text_log) + dat += "
    13. [l]
    14. " + + dat += "
      " + + if(!GLOB.round_text_log) + dat += "No-one has said anything this round! (How odd?)" + + var/datum/browser/popup = new(user, "dialoguelogs", "[src]", 550, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(user, "dialoguelogs") diff --git a/code/modules/admin/secrets/admin_secrets/prison_warp.dm b/code/modules/admin/secrets/admin_secrets/prison_warp.dm index d0d41cea5d..6f0732e781 100644 --- a/code/modules/admin/secrets/admin_secrets/prison_warp.dm +++ b/code/modules/admin/secrets/admin_secrets/prison_warp.dm @@ -30,7 +30,7 @@ H.drop_from_inventory(W) //teleport person to cell H.loc = pick(prisonwarp) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) else //teleport security person diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 3252219086..ca971c9b65 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -516,7 +516,28 @@ jobs += "" counter = 0 jobs += "" + //VOREStation Edit Start + //Exploration (Purple) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in planet_positions) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
      Exploration Positions
      [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
      " + //VOREstation Edit End //Civilian (Grey) counter = 0 jobs += "" @@ -670,6 +691,14 @@ var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title + //VOREStation Edit Start + if("explorationdept") + for(var/jobPos in planet_positions) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + //VOREStation Edit End if("civiliandept") for(var/jobPos in civilian_positions) if(!jobPos) continue @@ -1028,7 +1057,7 @@ M.loc = prison_cell if(istype(M, /mob/living/carbon/human)) var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) M << "You have been sent to the prison station!" diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index a6e50cb108..2b2a6672eb 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -490,6 +490,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(alert(usr, "You already have a ticket open. Is this for the same issue?",,"Yes","No") != "No") if(current_ticket) current_ticket.MessageNoRecipient(msg) + to_chat(usr, "PM to-Admins: [msg]") return else to_chat(usr, "Ticket not found, creating new one...") diff --git a/code/modules/admin/verbs/adminhelp_vr.dm b/code/modules/admin/verbs/adminhelp_vr.dm index 9f3aee4a83..f2df8e4eb2 100644 --- a/code/modules/admin/verbs/adminhelp_vr.dm +++ b/code/modules/admin/verbs/adminhelp_vr.dm @@ -1,8 +1,8 @@ -/datum/admin_help/proc/send2adminchat() +/datum/admin_help/proc/send2adminchat() if(!config.chat_webhook_url) return - var/list/adm = get_admin_counts() + var/list/adm = get_admin_counts() var/list/afkmins = adm["afk"] var/list/allmins = adm["total"] @@ -14,3 +14,25 @@ query_string += "&admin_number=[allmins.len]" query_string += "&admin_number_afk=[afkmins.len]" world.Export("[config.chat_webhook_url]?[query_string]") + +/client/verb/adminspice() + set category = "Admin" + set name = "Request Spice" + set desc = "Request admins to spice round up for you" + + //handle muting and automuting + if(prefs.muted & MUTE_ADMINHELP) + to_chat(usr, "Error: You cannot request spice (muted from adminhelps).") + return + + if(alert(usr, "Are you sure you want to request the admins spice things up for you? You accept the consequences if you do.",,"No","Yes") != "No") + message_admins("[ADMIN_FULLMONTY(usr)] has requested the round be spiced up a little.") + to_chat(usr, "You have requested some more spice in your round.") + else + to_chat(usr, "Spice request cancelled.") + return + + //if they requested spice, then remove spice verb temporarily to prevent spamming + usr.verbs -= /client/verb/adminspice + spawn(6000) + usr.verbs += /client/verb/adminspice // 10 minute cool-down for spice request diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 321655bb15..11541128c5 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -282,11 +282,11 @@ var/obj/item/device/pda/pda = H.wear_id id = pda.id id.icon_state = "gold" - id.access = get_all_accesses() + id.access = get_all_accesses().Copy() else var/obj/item/weapon/card/id/id = new/obj/item/weapon/card/id(M); id.icon_state = "gold" - id.access = get_all_accesses() + id.access = get_all_accesses().Copy() id.registered_name = H.real_name id.assignment = "Colony Director" id.name = "[id.registered_name]'s ID Card ([id.assignment])" diff --git a/code/modules/admin/verbs/lightning_strike.dm b/code/modules/admin/verbs/lightning_strike.dm index 6adb122d4a..1981e3f0d8 100644 --- a/code/modules/admin/verbs/lightning_strike.dm +++ b/code/modules/admin/verbs/lightning_strike.dm @@ -65,7 +65,8 @@ var/sound = get_sfx("thunder") for(var/mob/M in player_list) if((P && M.z in P.expected_z_levels) || M.z == T.z) - M.playsound_local(get_turf(M), soundin = sound, vol = 70, vary = FALSE, is_global = TRUE) + if(M.is_preference_enabled(/datum/client_preference/weather_sounds)) + M.playsound_local(get_turf(M), soundin = sound, vol = 70, vary = FALSE, is_global = TRUE) if(cosmetic) // Everything beyond here involves potentially damaging things. If we don't want to do that, stop now. return diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 7d0b7ee259..3ffd76878e 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -35,7 +35,7 @@ M.loc = pick(prisonwarp) if(istype(M, /mob/living/carbon/human)) var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) spawn(50) M << "You have been sent to the prison station!" @@ -368,7 +368,7 @@ Traitors and the like can also be revived with the previous role mostly intact. return var/location = alert(src,"Please specify where to spawn them.", "Location", "Right Here", "Arrivals", "Cancel") - if(!location) + if(location == "Cancel" || !location) return var/announce = alert(src,"Announce as if they had just arrived?", "Announce", "Yes", "No", "Cancel") @@ -491,7 +491,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if(equipment) if(charjob) job_master.EquipRank(new_character, charjob, 1) - equip_custom_items(new_character) + //equip_custom_items(new_character) //VOREStation Removal //If desired, add records. if(records) @@ -575,15 +575,9 @@ Traitors and the like can also be revived with the previous role mostly intact. return if(!customname) customname = "[using_map.company_name] Update" - for (var/obj/machinery/computer/communications/C in machines) - if(! (C.stat & (BROKEN|NOPOWER) ) ) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( C.loc ) - P.name = "'[command_name()] Update.'" - P.info = replacetext(input, "\n", "
      ") - P.update_space(P.info) - P.update_icon() - C.messagetitle.Add("[command_name()] Update") - C.messagetext.Add(P.info) + + //New message handling + post_comm_message(customname, replacetext(input, "\n", "
      ")) switch(alert("Should this be announced to the general population?",,"Yes","No")) if("Yes") diff --git a/code/modules/admin/verbs/randomverbs_vr.dm b/code/modules/admin/verbs/randomverbs_vr.dm new file mode 100644 index 0000000000..9fee7c58d0 --- /dev/null +++ b/code/modules/admin/verbs/randomverbs_vr.dm @@ -0,0 +1,74 @@ +/client/proc/spawn_character_mob() + set category = "Special Verbs" + set name = "Spawn Character As Mob" + set desc = "Spawn a specified ckey as a chosen mob." + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/client/picked_client = input(src, "Who are we spawning as a mob?", "Client", "Cancel") as null|anything in GLOB.clients + if(!picked_client) + return + var/list/types = typesof(/mob/living) + var/mob_type = input(src, "Mob path to spawn as?", "Mob") as text + if(!mob_type) + return + var/list/matches = new() + for(var/path in types) + if(findtext("[path]", mob_type)) + matches += path + if(matches.len==0) + return + var/mob/living/chosen + if(matches.len==1) + chosen = matches[1] + else + chosen = input("Select a mob type", "Select Mob", matches[1]) as null|anything in matches + if(!chosen) + return + + var/char_name = alert(src, "Spawn mob with their character name?", "Mob name", "Yes", "No", "Cancel") + var/name = 0 + if(char_name == "Cancel") + return + if(char_name == "Yes") + name = 1 + var/vorgans = alert(src, "Spawn mob with their character's vore organs and prefs?", "Vore organs", "Yes", "No", "Cancel") + var/organs + if(vorgans == "Cancel") + return + if(vorgans == "Yes") + organs = 1 + if(vorgans == "No") + organs = 0 + + var/spawnloc + if(!src.mob) + to_chat(src, "Can't spawn them in unless you're in a valid spawn location!") + return + spawnloc = get_turf(src.mob) + + var/mob/living/new_mob = new chosen(spawnloc) + + if(!new_mob) + to_chat(src, "Spawning failed, try again or bully coders") + return + new_mob.ai_holder_type = /datum/ai_holder/simple_mob/inert //Dont want the mob AI to activate if the client dc's or anything + + if(name) + new_mob.real_name = picked_client.prefs.real_name + new_mob.name = picked_client.prefs.real_name + + + new_mob.key = picked_client.key //Finally put them in the mob + if(organs) + new_mob.copy_from_prefs_vr() + + log_admin("[key_name_admin(src)] has spawned [new_mob.key] as mob [new_mob.type].") + message_admins("[key_name_admin(src)] has spawned [new_mob.key] as mob [new_mob.type].", 1) + + to_chat(new_mob, "You've been spawned as a mob! Have fun.") + + feedback_add_details("admin_verb","SCAM") //heh + + return new_mob \ No newline at end of file diff --git a/code/modules/admin/verbs/smite_vr.dm b/code/modules/admin/verbs/smite_vr.dm index 0d048dde2d..b0d4b8aa8c 100644 --- a/code/modules/admin/verbs/smite_vr.dm +++ b/code/modules/admin/verbs/smite_vr.dm @@ -18,7 +18,6 @@ feedback_add_details("admin_verb","SMITEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! switch(smite_choice) - /* if(SMITE_SHADEKIN_ATTACK) var/turf/Tt = get_turf(target) //Turf for target @@ -35,22 +34,22 @@ if(!Ts) return //Didn't find shadekin spawn turf - var/mob/living/simple_mob/shadekin/red/shadekin = new(Ts) + var/mob/living/simple_mob/shadekin/red/ai/shadekin = new(Ts) //Abuse of shadekin shadekin.real_name = shadekin.name shadekin.init_vore() shadekin.ability_flags |= 0x1 - shadekin.specific_targets = TRUE //Don't attack others shadekin.phase_shift() - shadekin.target_mob = target - shadekin.stance = STANCE_ATTACK + shadekin.ai_holder.give_target(target) + shadekin.ai_holder.hostile = FALSE + shadekin.ai_holder.mauling = TRUE shadekin.Life() //Remove when done spawn(10 SECONDS) if(shadekin) - shadekin.death()*/ //VORESTATION AI TEMPORARY REMOVAL + shadekin.death() - /*if(SMITE_SHADEKIN_NOMF) + if(SMITE_SHADEKIN_NOMF) var/list/kin_types = list( "Red Eyes (Dark)" = /mob/living/simple_mob/shadekin/red/dark, "Red Eyes (Light)" = /mob/living/simple_mob/shadekin/red/white, @@ -93,7 +92,6 @@ shadekin.real_name = shadekin.name shadekin.init_vore() shadekin.can_be_drop_pred = TRUE - shadekin.ai_inactive = TRUE shadekin.dir = SOUTH shadekin.ability_flags |= 0x1 shadekin.phase_shift() //Homf @@ -119,7 +117,7 @@ target.ghostize() qdel(target) qdel(shadekin) - */ + if(SMITE_REDSPACE_ABDUCT) redspace_abduction(target, src) diff --git a/code/modules/ai/aI_holder_subtypes/simple_mob_ai_vr.dm b/code/modules/ai/aI_holder_subtypes/simple_mob_ai_vr.dm index 9af6e67f1f..6c78d38d8f 100644 --- a/code/modules/ai/aI_holder_subtypes/simple_mob_ai_vr.dm +++ b/code/modules/ai/aI_holder_subtypes/simple_mob_ai_vr.dm @@ -1,5 +1,4 @@ -/datum/ai_holder/simple_mob/melee/evasive/returnhome - returns_home = 1 +#define VIRGO_AI_NORETURN_CREATE(x) x/returnhome/returns_home=1;x/returnhome/max_home_distance=8 -/datum/ai_holder/simple_mob/ranged/kiting/threatening/returnhome - returns_home = 1 \ No newline at end of file +VIRGO_AI_NORETURN_CREATE(/datum/ai_holder/simple_mob/melee/evasive) +VIRGO_AI_NORETURN_CREATE(/datum/ai_holder/simple_mob/ranged/kiting/threatening) \ No newline at end of file diff --git a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm index ed83a0896a..16609cc961 100644 --- a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm +++ b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm @@ -175,9 +175,8 @@ if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too. return TRUE // Monkeys are always food (sorry Pun Pun). else if(H.species && H.species.name == SPECIES_PROMETHEAN) // Prometheans are always our friends. - if(H in grudges) // Unless they're an ass. - return TRUE - return FALSE + if(!(H in grudges)) // Unless they're an ass. + return FALSE if(discipline && !rabid) return FALSE // We're a good slime. diff --git a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai_vr.dm b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai_vr.dm new file mode 100644 index 0000000000..c4bb314359 --- /dev/null +++ b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai_vr.dm @@ -0,0 +1,3 @@ +/datum/ai_holder/simple_mob/xenobio_slime/post_melee_attack(atom/A) + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + my_slime.a_intent = I_HELP // Return back to help after attacking \ No newline at end of file diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index 12cf9e2436..63154e4fe4 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -65,6 +65,11 @@ on_engagement(target) melee_attack(target) + else if(distance <= 1 && !holder.ICheckRangedAttack(target)) // Doesn't have projectile, but is pointblank + ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE) + on_engagement(target) + melee_attack(target) + // Shoot them. else if(holder.ICheckRangedAttack(target) && (distance <= max_range(target)) ) on_engagement(target) diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index b13d4d6844..e70e991d6e 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -4,6 +4,7 @@ var/hostile = FALSE // Do we try to hurt others? var/retaliate = FALSE // Attacks whatever struck it first. Mobs will still attack back if this is false but hostile is true. var/mauling = FALSE // Attacks unconscious mobs + var/handle_corpse = FALSE // Allows AI to acknowledge corpses (e.g. nurse spiders) var/atom/movable/target = null // The thing (mob or object) we're trying to kill. var/atom/movable/preferred_target = null// If set, and if given the chance, we will always prefer to target this over other options. @@ -24,7 +25,7 @@ // Step 1, find out what we can see. /datum/ai_holder/proc/list_targets() - . = hearers(vision_range, holder) - src // Remove ourselves to prevent suicidal decisions. + . = hearers(vision_range, holder) - holder // Remove ourselves to prevent suicidal decisions. ~ SRC is the ai_holder. var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha)) @@ -121,7 +122,7 @@ if(L.key && !L.client) // SSD players get a pass return FALSE if(L.stat) - if(L.stat == DEAD) // Leave dead things alone + if(L.stat == DEAD && !handle_corpse) // Leave dead things alone return FALSE if(L.stat == UNCONSCIOUS) // Do we have mauling? Yes? Then maul people who are sleeping but not SSD if(mauling) @@ -266,4 +267,4 @@ /datum/ai_holder/proc/lose_taunt() ai_log("lose_taunt() : Resetting preferred_target.", AI_LOG_INFO) - preferred_target = null \ No newline at end of file + preferred_target = null diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index d7a7cdf713..22b069093a 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -50,6 +50,11 @@ /datum/alarm_handler/proc/major_alarms() return visible_alarms() +/datum/alarm_handler/proc/has_major_alarms() + if(alarms && alarms.len) + return 1 + return 0 + /datum/alarm_handler/proc/minor_alarms() return visible_alarms() diff --git a/code/modules/artifice/cursedform.dm b/code/modules/artifice/cursedform.dm new file mode 100644 index 0000000000..17f8fbf618 --- /dev/null +++ b/code/modules/artifice/cursedform.dm @@ -0,0 +1,40 @@ +/obj/item/weapon/paper/carbon/cursedform + name = "Form - Inventory Requisition r10.7.1E" + +/obj/item/weapon/paper/carbon/cursedform/Initialize() + ..() + info = {"

      Form - Inventory Requisition r10.7.1E

      General Request Form


      General


      Name:
      Department:
      Departmental Rank:
      Organization(If not Nanotrasen):
      Date:



      Requested Item(s):
      Quantity:
      Reason for request:
      Is this replacement equipment?:
      If `Yes`; above, specify equiment and reason for replacement:


      Authorization


      Authorizing Department:
      Authorizing Dept. Head:


      Contact and Delivery


      EPv2 Address of requesting party(Do not leave blank):
      Delivery location or department:


      Nanotrasen Employee Identification Number:
      Signature of Requester and Date





      Authorizor`s Nanotrasen Employee Identification Number:
      Authorizing Signature and Date(Include authorizing department`s stamp below)




      Shipping Department Only

      (Do not write below this line)

      Nanotrasen Purchasing Approval Code:
      Nanotrasen Employee Identification Number:
      Receiving Shipping Employee:
      Signature and Date


      "} + info_links = {"

      Form - Inventory Requisition r10.7.1E

      General Request Form


      General


      Name: write
      Department: write
      Departmental Rank: write
      Organization(If not Nanotrasen): write
      Date: write



      Requested Item(s): write
      Quantity: write
      Reason for request: write
      Is this replacement equipment?: write
      If `Yes` above, specify equiment and reason for replacement: write


      Authorization


      Authorizing Department: write
      Authorizing Dept. Head: write


      Contact and Delivery


      EPv2 Address of requesting party(Do not leave blank): write
      Delivery location or department: write


      Nanotrasen Employee Identification Number: write
      Signature of Requester and Date

      write



      Authorizor`s Nanotrasen Employee Identification Number: write
      Authorizing Signature and Date(Include authorizing department`s stamp below)

      write


      Shipping Department Only

      (Do not write below this line)

      Nanotrasen Purchasing Approval Code: write
      Nanotrasen Employee Identification Number: write
      Receiving Shipping Employee: write
      Signature and Date

      write
      write"} + +/obj/item/weapon/paper/carbon/cursedform/AltClick() // No fun. + return + +/obj/item/weapon/paper/carbon/cursedform/burnpaper(obj/item/weapon/flame/P, mob/user) + var/class = "warning" + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + + if(P.lit && !user.restrained()) + if(istype(P, /obj/item/weapon/flame/lighter/zippo)) + class = "rose" + + user.visible_message("[user] holds \the [P] up to \the [src], it looks like [TU.hes] trying to burn it!", \ + "You hold \the [P] up to \the [src], burning it slowly.") + + if(do_after(user, 2 SECONDS, src) && P.lit) + user.visible_message("[user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ + "You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") + + if(user.get_inactive_hand() == src) + user.drop_from_inventory(src) + + new /obj/effect/decal/cleanable/ash(src.loc) + qdel(src) + + else + to_chat(user,"You must hold \the [P] steady to burn \the [src].") + + if(isliving(user)) + var/mob/living/L = user + L.visible_message("[L] convulses, the very letters of \the [src] searing themselves into their eyes!", \ + "You convulse, the very letters of \the [src] searing themselves into your eyes!") + L.add_modifier(/datum/modifier/grievous_wounds, 10 MINUTES) diff --git a/code/modules/artifice/telecube.dm b/code/modules/artifice/telecube.dm new file mode 100644 index 0000000000..bb2af4b83d --- /dev/null +++ b/code/modules/artifice/telecube.dm @@ -0,0 +1,236 @@ +/* + * Home of the telecube. + */ + +/datum/category_item/catalogue/anomalous/precursor_a/telecube + name = "Quantomatically Entangled Digicube" + + desc = "An enigmatic cube that appears superficially similar to a Positronic Cube. \ + However, the similarities hopefully end there, as this device emits no sound during \ + operation or observation. Its alloy composition is unknown, though it is incredibly \ + dense, as it resists any and all forms of radiation.
      \ + Upon physical contact, however, the device will translocate the offending entity to a \ + matching twin cube, generating no detectable radiation. This process occurs at speeds \ + unmatched even by modern predictions of Bluespace technology, and with no visible power \ + source." + + value = CATALOGUER_REWARD_HARD + +// Standard one needs to be smacked onto another one to link together. +/obj/item/weapon/telecube + name = "locus" + desc = "A strange metallic cube that pulses silently." + description_info = "Ctrl-Clicking on this object will attempt to activate its unique ability." + icon = 'icons/obj/props/telecube.dmi' + icon_state = "cube" + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_MATERIAL = 7, TECH_POWER = 6, TECH_BLUESPACE = 7, TECH_ANOMALY = 2, TECH_PRECURSOR = 2) + + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/telecube) + + slowdown = 5 + + throw_range = 2 + + var/obj/item/weapon/telecube/mate = null + + var/start_paired = FALSE + var/mirror_colors = FALSE + + var/randomize_colors = FALSE + + var/glow_color = "#FFFFFF" + var/image/glow = null + var/image/charge = null + + var/shell_color = "#FFFFFF" + var/image/shell = null + + var/cooldown_time = 30 SECONDS + var/last_teleport = 0 + +// How far the cube will search for things to teleport. 0 = only contacting objects / mobs. + var/teleport_range = 0 // For all that is holy, do not change this unless you know what you're doing. + + var/omniteleport = FALSE // Will this teleport anchored things too? + +/obj/item/weapon/telecube/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + last_teleport = world.time + + glow = image(icon = icon, icon_state = "[icon_state]-ready") + glow.plane = PLANE_LIGHTING_ABOVE + charge = image(icon = icon, icon_state = "[icon_state]-charging") + charge.plane = PLANE_LIGHTING_ABOVE + shell = image(icon = icon, icon_state = "[icon_state]") + + if(teleport_range) + description_info += "
      " + description_info += "Alt-Clicking on this object will utilize its second unique ability." + + if(randomize_colors) + glow_color = rgb(rand(0, 255),rand(0, 255),rand(0, 255)) + shell_color = rgb(rand(0, 255),rand(0, 255),rand(0, 255)) + + if(start_paired) + mate = new(src.loc) + if(mirror_colors) + mate.glow_color = shell_color + mate.shell_color = glow_color + else + mate.glow_color = glow_color + mate.shell_color = shell_color + mate.pair_cube(src) + + glow.color = glow_color + charge.color = glow_color + shell.color = shell_color + + return + +/obj/item/weapon/telecube/process() + ..() + update_icon() + +/obj/item/weapon/telecube/update_icon() + . = ..() + glow.color = glow_color + charge.color = glow_color + shell.color = shell_color + + if(shell.color != initial(shell.color)) + cut_overlay(shell) + add_overlay(shell) + + if(world.time < (last_teleport + cooldown_time)) + cut_overlay(charge) + cut_overlay(glow) + add_overlay(charge) + else + cut_overlay(glow) + cut_overlay(charge) + add_overlay(glow) + +/obj/item/weapon/telecube/Destroy() + STOP_PROCESSING(SSobj, src) + if(mate) + var/turf/T = get_turf(mate) + mate.visible_message("\The [mate] collapses into itself!") + mate.mate = null + mate = null + explosion(T,1,3,7) + + ..() + +/obj/item/weapon/telecube/proc/pair_cube(var/obj/item/weapon/telecube/M) + if(mate) + return 0 + else + mate = M + update_icon() + return 1 + +/obj/item/weapon/telecube/proc/teleport_to_mate(var/atom/movable/A, var/areaporting = FALSE) + . = FALSE + + if(!A) + return . + + if(A == src || A == mate) + A.visible_message("\The [A] distorts and fades, before popping back into existence.") + return . + + var/mob/living/L = src.loc + + if(istype(L)) + L.drop_from_inventory(src) + forceMove(get_turf(src)) + + if(world.time < (last_teleport + cooldown_time)) + return . + + if((A.anchored && !omniteleport) || !mate) + A.visible_message("\The [A] distorts for a moment, before reforming in the same position.") + return . + + var/turf/TLocate = get_turf(mate) + + var/turf/T1 = get_turf(locate(TLocate.x + (A.x - x), TLocate.y + (A.y - y), TLocate.z)) + + if(T1) + A.visible_message("\The [A] fades out of existence.") + A.forceMove(T1) + . = TRUE + A.visible_message("\The [A] fades into existence.") + else + return . + + if(teleport_range && !areaporting) + for(var/atom/movable/M in orange(teleport_range, A)) + teleport_to_mate(M, TRUE) + +/obj/item/weapon/telecube/proc/swap_with_mate() + . = FALSE + + if(!mate || !teleport_range) + return . + + var/list/objects_near_me = range(teleport_range, get_turf(src)) + var/list/objects_near_mate = range(teleport_range, get_turf(mate)) + + for(var/atom/movable/M in objects_near_me) + teleport_to_mate(M, TRUE) + + for(var/atom/movable/M1 in objects_near_mate) + mate.teleport_to_mate(M1, TRUE) + + . = TRUE + return . + +/obj/item/weapon/telecube/CtrlClick(mob/user) + if(Adjacent(user)) + if(teleport_to_mate(user)) + last_teleport = world.time + return + +/obj/item/weapon/telecube/AltClick(mob/user) + if(Adjacent(user)) + if(swap_with_mate()) + last_teleport = world.time + mate.last_teleport = world.time + return + +/obj/item/weapon/telecube/Bump(atom/movable/AM) + if(teleport_to_mate(AM)) + last_teleport = world.time + . = ..() + +/obj/item/weapon/telecube/Bumped(atom/movable/M as mob|obj) + if(teleport_to_mate(M)) + last_teleport = world.time + . = ..() + +// Subtypes + +/obj/item/weapon/telecube/mated + start_paired = TRUE + +/obj/item/weapon/telecube/randomized + randomize_colors = TRUE + +/obj/item/weapon/telecube/randomized/mated + start_paired = TRUE + +/obj/item/weapon/telecube/precursor + glow_color = "#FF1D8E" + shell_color = "#2F1B26" + +/obj/item/weapon/telecube/precursor/mated + start_paired = TRUE + +/obj/item/weapon/telecube/precursor/mated/zone + teleport_range = 2 + +/obj/item/weapon/telecube/precursor/mated/mirrorcolor + mirror_colors = TRUE diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index 1182129cd6..bf1165d2ea 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -62,4 +62,10 @@ proc/createRandomZlevel() /obj/effect/landmark/gateway_scatter/Initialize() . = ..() awaydestinations += src + +/obj/effect/landmark/event_scatter + name = "uncalibrated gateway destination" +/obj/effect/landmark/event_scatter/Initialize() + . = ..() + eventdestinations += src //VOREStation Add End diff --git a/code/modules/blob/blob.dm b/code/modules/blob/blob.dm index c9ab387753..877c684b5e 100644 --- a/code/modules/blob/blob.dm +++ b/code/modules/blob/blob.dm @@ -40,7 +40,7 @@ else icon_state = "blob_damaged" -/obj/effect/blob/proc/take_damage(var/damage) +/obj/effect/blob/take_damage(var/damage) // VOREStation Edit health -= damage if(health < 0) playsound(loc, 'sound/effects/splat.ogg', 50, 1) diff --git a/code/modules/blob2/blobs/core.dm b/code/modules/blob2/blobs/core.dm index 17ecd2429f..f43ec7ad6f 100644 --- a/code/modules/blob2/blobs/core.dm +++ b/code/modules/blob2/blobs/core.dm @@ -6,6 +6,7 @@ var/list/blob_cores = list() icon = 'icons/mob/blob.dmi' icon_state = "blank_blob" desc = "A huge, pulsating yellow mass." + density = TRUE //bandaid fix for PolarisSS13/6173 max_integrity = 150 point_return = -1 health_regen = 0 //we regen in Life() instead of when pulsed diff --git a/code/modules/blob2/overmind/overmind.dm b/code/modules/blob2/overmind/overmind.dm index 6477c86f22..223cb41e60 100644 --- a/code/modules/blob2/overmind/overmind.dm +++ b/code/modules/blob2/overmind/overmind.dm @@ -9,7 +9,6 @@ var/list/overminds = list() mouse_opacity = 1 see_in_dark = 8 invisibility = INVISIBILITY_OBSERVER - layer = FLY_LAYER + 0.1 faction = "blob" var/obj/structure/blob/core/blob_core = null // The blob overmind's core diff --git a/code/modules/blob2/overmind/types.dm b/code/modules/blob2/overmind/types.dm index 6049ccfc72..49b968cfcc 100644 --- a/code/modules/blob2/overmind/types.dm +++ b/code/modules/blob2/overmind/types.dm @@ -593,7 +593,7 @@ attack_verb = "splashes" /datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B) - radiation_repository.radiate(B, 200) + SSradiation.radiate(B, 200) /datum/blob_type/volatile_alluvium name = "volatile alluvium" diff --git a/code/modules/catalogue/cataloguer.dm b/code/modules/catalogue/cataloguer.dm index 60e86eadee..f1c5756443 100644 --- a/code/modules/catalogue/cataloguer.dm +++ b/code/modules/catalogue/cataloguer.dm @@ -312,3 +312,15 @@ GLOBAL_LIST_EMPTY(all_cataloguers) interact(usr) // So it refreshes the window. return 1 +/obj/item/device/cataloguer/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/card/id) && !busy) + busy = TRUE + var/obj/item/weapon/card/id/ID = W + if(points_stored) + ID.survey_points += points_stored + points_stored = 0 + to_chat(user, "You swipe the id over \the [src].") + else + to_chat(user, "\The [src] has no points available.") + busy = FALSE + return ..() diff --git a/code/modules/catalogue/cataloguer_vr.dm b/code/modules/catalogue/cataloguer_vr.dm new file mode 100644 index 0000000000..d1dfac5dea --- /dev/null +++ b/code/modules/catalogue/cataloguer_vr.dm @@ -0,0 +1,56 @@ +/obj/item/device/cataloguer/compact + name = "compact cataloguer" + icon = 'icons/vore/custom_items_vr.dmi' + icon_state = "tricorder" + action_button_name = "Toggle Cataloguer" + var/deployed = TRUE + scan_range = 1 + toolspeed = 1.2 + +/obj/item/device/cataloguer/compact/update_icon() + if(busy) + icon_state = "[initial(icon_state)]_s" + else + icon_state = initial(icon_state) + +/obj/item/device/cataloguer/compact/ui_action_click() + toggle() + +/obj/item/device/cataloguer/compact/verb/toggle() + set name = "Toggle Cataloguer" + set category = "Object" + + if(busy) + to_chat(usr, span("warning", "\The [src] is currently scanning something.")) + return + deployed = !(deployed) + if(deployed) + w_class = ITEMSIZE_NORMAL + icon_state = "[initial(icon_state)]" + to_chat(usr, span("notice", "You flip open \the [src].")) + else + w_class = ITEMSIZE_SMALL + icon_state = "[initial(icon_state)]_closed" + to_chat(usr, span("notice", "You close \the [src].")) + + if (ismob(usr)) + var/mob/M = usr + M.update_action_buttons() + +/obj/item/device/cataloguer/compact/afterattack(atom/target, mob/user, proximity_flag) + if(!deployed) + to_chat(user, span("warning", "\The [src] is closed.")) + return + return ..() + +/obj/item/device/cataloguer/compact/pulse_scan(mob/user) + if(!deployed) + to_chat(user, span("warning", "\The [src] is closed.")) + return + return ..() + +/obj/item/device/cataloguer/compact/pathfinder + name = "pathfinder's cataloguer" + icon_state = "tricorder_med" + scan_range = 3 + toolspeed = 1 diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 841838dc6a..457f9800ea 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -139,7 +139,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else O.robotize() - for(var/name in list(O_HEART,O_EYES,O_LUNGS,O_LIVER,O_KIDNEYS,O_BRAIN)) + for(var/name in list(O_HEART,O_EYES,O_VOICE,O_LUNGS,O_LIVER,O_KIDNEYS,O_SPLEEN,O_STOMACH,O_INTESTINE,O_BRAIN)) var/status = pref.organ_data[name] if(!status) continue @@ -237,6 +237,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O organ_name = "heart" if(O_EYES) organ_name = "eyes" + if(O_VOICE) + organ_name = "larynx" if(O_BRAIN) organ_name = "brain" if(O_LUNGS) @@ -245,6 +247,12 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O organ_name = "liver" if(O_KIDNEYS) organ_name = "kidneys" + if(O_SPLEEN) + organ_name = "spleen" + if(O_STOMACH) + organ_name = "stomach" + if(O_INTESTINE) + organ_name = "intestines" if(status == "cyborg") ++ind @@ -383,6 +391,16 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O if(!pref.species_preview || !(pref.species_preview in all_species)) return TOPIC_NOACTION + var/datum/species/setting_species + + if(all_species[href_list["set_species"]]) + setting_species = all_species[href_list["set_species"]] + else + return TOPIC_NOACTION + + if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) //VOREStation Edit: selectability + return TOPIC_NOACTION + var/prev_species = pref.species pref.species = href_list["set_species"] if(prev_species != pref.species) @@ -698,7 +716,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["organs"]) - var/organ_name = input(user, "Which internal function do you want to change?") as null|anything in list("Heart", "Eyes", "Lungs", "Liver", "Kidneys", "Brain") + var/organ_name = input(user, "Which internal function do you want to change?") as null|anything in list("Heart", "Eyes","Larynx", "Lungs", "Liver", "Kidneys", "Spleen", "Intestines", "Stomach", "Brain") if(!organ_name) return var/organ = null @@ -707,12 +725,20 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O organ = O_HEART if("Eyes") organ = O_EYES + if("Larynx") + organ = O_VOICE if("Lungs") organ = O_LUNGS if("Liver") organ = O_LIVER if("Kidneys") organ = O_KIDNEYS + if("Spleen") + organ = O_SPLEEN + if("Intestines") + organ = O_INTESTINE + if("Stomach") + organ = O_STOMACH if("Brain") if(pref.organ_data[BP_HEAD] != "cyborg") user << "You may only select a cybernetic or synthetic brain if you have a full prosthetic body." @@ -808,7 +834,12 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O dat += "

      [current_species.name] \[change\]


      " dat += "
      " dat += "" - dat += "" + //vorestation edit begin + if(current_species.wikilink) + dat += "" + else + dat += "" + //vorestation edit end dat += "" for(var/gear_name in LC.gear) var/datum/gear/G = LC.gear[gear_name] + if(G.ckeywhitelist && !(preference_mob.ckey in G.ckeywhitelist)) //Vorestation Edit + continue //Vorestation Edit + if(G.character_name && !(preference_mob.client.prefs.real_name in G.character_name)) //Vorestation Edit + continue //Vorestation Edit var/ticked = (G.display_name in pref.gear) . += "" . += "" diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm index 4c6066c240..9f008afe08 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm @@ -34,7 +34,7 @@ display_name = "wallet, polychromic" path = /obj/item/weapon/storage/wallet/poly cost = 0 //VOREStation Edit - + /datum/gear/accessory/wallet/womens display_name = "wallet, womens" @@ -209,6 +209,8 @@ ..() var/list/sweaters = list() for(var/sweater in typesof(/obj/item/clothing/accessory/sweater)) + if(sweater in typesof(/obj/item/clothing/accessory/sweater/fluff)) //VOREStation addition + continue //VOREStation addition var/obj/item/clothing/suit/sweater_type = sweater sweaters[initial(sweater_type.name)] = sweater_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(sweaters)) diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm index c3d4adc34b..63b32a2707 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm @@ -30,34 +30,42 @@ display_name = "collar, holo" path = /obj/item/clothing/accessory/collar/holo +//TFF 17/6/19 - public loadout addition: Indigestible Holocollar +/datum/gear/collar/holo/indigestible + display_name = "collar, holo (indigestible)" + path = /obj/item/clothing/accessory/collar/holo/indigestible + /datum/gear/accessory/holster display_name = "holster selection (Security, CD, HoP, Exploration)" allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Explorer","Pathfinder") /datum/gear/accessory/brown_vest - display_name = "webbing, brown" - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + display_name = "webbing, brown (Eng, Sec, Med, Exploration, Miner)" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder","Shaft Miner") /datum/gear/accessory/black_vest - display_name = "webbing, black" - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + display_name = "webbing, black (Eng, Sec, Med, Exploration, Miner)" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder","Shaft Miner") /datum/gear/accessory/white_vest display_name = "webbing, white (Medical)" allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic") /datum/gear/accessory/brown_drop_pouches - display_name = "drop pouches, brown" - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + display_name = "drop pouches, brown (Eng, Sec, Med, Exploration, Miner)" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder","Shaft Miner") /datum/gear/accessory/black_drop_pouches - display_name = "drop pouches, black" - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + display_name = "drop pouches, black (Eng, Sec, Med, Exploration, Miner)" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder","Shaft Miner") /datum/gear/accessory/white_drop_pouches display_name = "drop pouches, white (Medical)" allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic") +/datum/gear/accessory/webbing + cost = 1 + /datum/gear/accessory/stethoscope allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") @@ -67,15 +75,21 @@ description = "A small necklace device that will notify an offsite cloning facility should you expire after activating it." /datum/gear/accessory/tronket - display_name = "metal necklace" - description = "A shiny steel chain with a vague metallic object dangling off it." - path = /obj/item/clothing/accessory/tronket + display_name = "metal necklace" + description = "A shiny steel chain with a vague metallic object dangling off it." + path = /obj/item/clothing/accessory/tronket + +/datum/gear/accessory/pilotpin + display_name = "pilot qualification pin" + description = "An iron pin denoting the qualification to fly SCG spacecraft." + path = /obj/item/clothing/accessory/solgov/specialty/pilot + allowed_roles = list("Pathfinder", "Pilot", "Field Medic") /datum/gear/accessory/flops - display_name = "drop straps" - description = "Wearing suspenders over shoulders? That's been so out for centuries and you know better." - path = /obj/item/clothing/accessory/flops + display_name = "drop straps" + description = "Wearing suspenders over shoulders? That's been so out for centuries and you know better." + path = /obj/item/clothing/accessory/flops /datum/gear/accessory/flops/New() ..() - gear_tweaks = list(gear_tweak_free_color_choice) \ No newline at end of file + gear_tweaks = list(gear_tweak_free_color_choice) diff --git a/code/modules/client/preference_setup/loadout/loadout_ears.dm b/code/modules/client/preference_setup/loadout/loadout_ears.dm index d92b2358b4..f80e60acaf 100644 --- a/code/modules/client/preference_setup/loadout/loadout_ears.dm +++ b/code/modules/client/preference_setup/loadout/loadout_ears.dm @@ -11,4 +11,32 @@ /datum/gear/ears/circuitry display_name = "earwear, circuitry (empty)" - path = /obj/item/clothing/ears/circuitry \ No newline at end of file + path = /obj/item/clothing/ears/circuitry + + +/datum/gear/ears/earrings + display_name = "earring selection" + description = "A selection of eye-catching earrings." + path = /obj/item/clothing/ears/earring + +/datum/gear/ears/earrings/New() + ..() + var/earrings = list() + earrings["stud, pearl"] = /obj/item/clothing/ears/earring/stud + earrings["stud, glass"] = /obj/item/clothing/ears/earring/stud/glass + earrings["stud, wood"] = /obj/item/clothing/ears/earring/stud/wood + earrings["stud, iron"] = /obj/item/clothing/ears/earring/stud/iron + earrings["stud, steel"] = /obj/item/clothing/ears/earring/stud/steel + earrings["stud, silver"] = /obj/item/clothing/ears/earring/stud/silver + earrings["stud, gold"] = /obj/item/clothing/ears/earring/stud/gold + earrings["stud, platinum"] = /obj/item/clothing/ears/earring/stud/platinum + earrings["stud, diamond"] = /obj/item/clothing/ears/earring/stud/diamond + earrings["dangle, glass"] = /obj/item/clothing/ears/earring/dangle/glass + earrings["dangle, wood"] = /obj/item/clothing/ears/earring/dangle/wood + earrings["dangle, iron"] = /obj/item/clothing/ears/earring/dangle/iron + earrings["dangle, steel"] = /obj/item/clothing/ears/earring/dangle/steel + earrings["dangle, silver"] = /obj/item/clothing/ears/earring/dangle/silver + earrings["dangle, gold"] = /obj/item/clothing/ears/earring/dangle/gold + earrings["dangle, platinum"] = /obj/item/clothing/ears/earring/dangle/platinum + earrings["dangle, diamond"] = /obj/item/clothing/ears/earring/dangle/diamond + gear_tweaks += new/datum/gear_tweak/path(earrings) diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm new file mode 100644 index 0000000000..13c81b63e1 --- /dev/null +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm @@ -0,0 +1,899 @@ +/datum/gear/fluff + path = /obj/item + sort_category = "Fluff Items" + display_name = "If this item can be chosen or seen, ping a coder immediately!" + ckeywhitelist = list("This entry should never be choosable with this variable set.") //If it does, then that means somebody fucked up the whitelist system pretty hard + character_name = list("This entry should never be choosable with this variable set.") + cost = 0 +/* +/datum/gear/fluff/testhorn + path = /obj/item/weapon/bikehorn + display_name = "Airhorn - Example Item" + description = "An example item that you probably shouldn't see!" + ckeywhitelist = list("mewchild") + allowed_roles = list("Station Engineer") +*/ + +// 0-9 CKEYS +/datum/gear/fluff/malady_crop + path = /obj/item/weapon/material/twohanded/fluff/riding_crop/malady + display_name = "Malady's Crop" + ckeywhitelist = list("1r1s") + character_name = list("Malady Blanche") + +// A CKEYS +/datum/gear/fluff/lethe_helmet + path = /obj/item/clothing/head/helmet/hos/fluff/lethe + display_name = "Lethe's Helmet" + ckeywhitelist = list("adk09") + character_name = list("Lethe") + +/datum/gear/fluff/xander_bracer + path = /obj/item/clothing/accessory/bracer/fluff/xander_sthasha + display_name = "Xander's Bracer" + ckeywhitelist = list("aegisoa") + character_name = list("Xander Bevin") + +/datum/gear/fluff/lynn_penlight + path = /obj/item/device/flashlight/pen/fluff/lynn + display_name = "Lynn's Penlight" + ckeywhitelist = list("argobargsoup") + character_name = list("Lynn Shady") + +/datum/gear/fluff/aronai_ccmeduniform + path = /obj/item/clothing/under/solgov/utility/sifguard/officer/medical + display_name = "centcom medical uniform" + description = "A medical uniform straight from Central Command." + ckeywhitelist = list("arokha") + character_name = list("Aronai Kadigan") + +/datum/gear/fluff/aronai_ccmedjacket + path = /obj/item/clothing/suit/storage/service/sifguard/medical/command + display_name = "centcom medical jacket" + description = "A medical jacket straight from Central Command." + ckeywhitelist = list("arokha") + character_name = list("Aronai Kadigan") + +// B CKEYS +/datum/gear/fluff/yuuko_kimono + path = /obj/item/clothing/under/fluff/sakura_hokkaido_kimono + display_name = "Yuuko's Kimono" + ckeywhitelist = list("benemuel") + character_name = list("Yuuko Shimmerpond") + +/datum/gear/fluff/cassandra_box + path = /obj/item/weapon/storage/box/fluff/cassandra + display_name = "Cassandra's Box" + ckeywhitelist = list("beyondmylife") + character_name = list("Cassandra Selone") + +/datum/gear/fluff/kilano_dress + path = /obj/item/clothing/under/dress/fluff/kilano + display_name = "Kilano's Dress" + ckeywhitelist = list("beyondmylife") + character_name = list("Kilano Soryu") + +/datum/gear/fluff/kilano_gloves + path = /obj/item/clothing/gloves/fluff/kilano + display_name = "Kilano's Gloves" + ckeywhitelist = list("beyondmylife") + character_name = list("Kilano Soryu") + +/datum/gear/fluff/netra_box + path = /obj/item/weapon/storage/box/fluff/kilano + display_name = "Ne'tra's Box" + ckeywhitelist = list("beyondmylife") + character_name = list("Ne'tra Ky'ram") + +/datum/gear/fluff/xin_sovietuniform + path = /obj/item/clothing/under/soviet + display_name = "Xin's Soviet Uniform" + ckeywhitelist = list("britishrabbit") + character_name = list("Xin Xiao") + description = "This soviet uniform has seen considerable use over the years, it's rather worn in some places, frayed in others and the stomach region has signs of being stretched out repeatedly." + +/datum/gear/fluff/tasald_box + path = /obj/item/weapon/storage/box/fluff/tasald + display_name = "Tasald's Box" + ckeywhitelist = list("bwoincognito") + character_name = list("Tasald Corlethian") + +/datum/gear/fluff/octavius_box + path = /obj/item/weapon/storage/box/fluff/octavious + display_name = "Octavious' Box" + ckeywhitelist = list("bwoincognito") + character_name = list("Octavious Ward") + +/datum/gear/fluff/jayda_meduniform + path = /obj/item/clothing/under/solgov/utility/sifguard/medical/fluff + display_name = "Jayda's Uniform" + ckeywhitelist = list("burritojustice") + character_name = list("Jayda Wilson") + +// C CKEYS +/datum/gear/fluff/james_disk + path = /obj/item/weapon/disk/data + display_name = "James' Disk" + ckeywhitelist = list("cockatricexl") + character_name = list("James Holder") + +/datum/gear/fluff/jasmine_implant + path = /obj/item/weapon/implanter/reagent_generator/jasmine + display_name = "Jasmine's Implant" + ckeywhitelist = list("cameron653") + character_name = list("Jasmine Lizden") + +/datum/gear/fluff/diana_robe + path = /obj/item/clothing/suit/fluff/purp_robes + display_name = "Diana's Robes" + ckeywhitelist = list("cameron653") + character_name = list("Diana Kuznetsova") + +/datum/gear/fluff/diana_tiara + path = /obj/item/clothing/head/fluff/pink_tiara + display_name = "Diana's Tiara" + ckeywhitelist = list("cameron653") + character_name = list("Diana Kuznetsova") + +/datum/gear/fluff/aika_coat + path = /obj/item/clothing/suit/fluff/blue_trimmed_coat + display_name = "Aika's Coat" + ckeywhitelist = list("chaoko99") + character_name = list("Aika Hisakawa") + +/datum/gear/fluff/sariU_disk + path = /obj/item/weapon/disk/limb/eggnerdltd + display_name = "Sari-U's Eggnerd Disk" + ckeywhitelist = list("crossexonar") + character_name = list("Sari-U") + +/datum/gear/fluff/sariE_disk + path = /obj/item/weapon/disk/limb/eggnerdltd + display_name = "Sari-E's Eggnerd Disk" + ckeywhitelist = list("crossexonar") + character_name = list("Sari-E") + +// D CKEYS +/datum/gear/fluff/dhaeleena_medal + path = /obj/item/clothing/accessory/medal/silver/security/fluff/dhael + display_name = "Dhaeleena's Medal" + ckeywhitelist = list("dhaeleena") + character_name = list("Dhaeleena M'iar") + +/datum/gear/fluff/elliot_belt + path = /obj/item/weapon/storage/belt/champion + display_name = "Elliot's Belt" + ckeywhitelist = list("dickfreedomjohnson") + character_name = list("Elliot Richards") + +/datum/gear/fluff/drake_box + path = /obj/item/weapon/storage/box/fluff/drake + display_name = "Drake's Box" + ckeywhitelist = list("drakefrostpaw") + character_name = list("Drake Frostpaw") + +/datum/gear/fluff/theseus_coin + path = /obj/item/weapon/coin/diamond + display_name = "Theseus' Diamond coin" + ckeywhitelist = list("draycu") + character_name = list("Theseus") + description = "An engraved coin made of diamond. On the side for heads is printed the year 2541, along with the letter T. On the side for tails, the letter Y can be seen." + +/datum/gear/fluff/yonra_box + path = /obj/item/weapon/storage/box/fluff/yonra + display_name = "Yonra's Box" + ckeywhitelist = list("draycu") + character_name = list("Schae Yonra") + +// E CKEYS +/datum/gear/fluff/serkii_slippers + path = /obj/item/clothing/shoes/slippers + display_name = "Serkii's Slippers" + ckeywhitelist = list("eekasqueak") + character_name = list("Serkii Miishy") + +/datum/gear/fluff/serkii_skirt + path = /obj/item/clothing/under/skirt/fluff/serkii + display_name = "Serkii's Skirt" + ckeywhitelist = list("eekasqueak") + character_name = list("Serkii Miishy") + +/datum/gear/fluff/jessie_coat + path = /obj/item/clothing/suit/storage/hooded/wintercoat/jessie + display_name = "Jessie's Coat" + ckeywhitelist = list("epiccharger") + character_name = list("Jessie Mare") + +/datum/gear/fluff/verd_medal + path = /obj/item/clothing/accessory/medal/bronze_heart + display_name = "Verd's Medal" + ckeywhitelist = list("epigraphzero") + character_name = list("Verd Woodrow") + +// F CKEYS + +// G CKEYS +/datum/gear/fluff/eldi_implant + path = /obj/item/weapon/implanter/reagent_generator/eldi + display_name = "Eldi's Implant" + ckeywhitelist = list("gowst") + character_name = list("Eldi Moljir") + +// H CKEYS +/datum/gear/fluff/lauren_medal + path = /obj/item/clothing/accessory/medal/conduct + display_name = "Lauren's Medal" + ckeywhitelist = list("heroman3003") + character_name = list("Lauren Zackson") + +/datum/gear/fluff/lauren_string + path = /obj/item/clothing/accessory/collar/fluff/goldenstring + display_name = "Lauren's String" + ckeywhitelist = list("heroman3003") + character_name = list("Lauren Zackson") + +/datum/gear/fluff/belle_sizegun + path = /obj/item/weapon/gun/energy/sizegun + display_name = "Belle's Sizegun" + ckeywhitelist = list("hottokeeki") + character_name = list("Belle Day") + +/datum/gear/fluff/belle_implant + path = /obj/item/weapon/implanter/reagent_generator/belle + display_name = "Belle's Implant" + ckeywhitelist = list("hottokeeki") + character_name = list("Belle Day") + +// I CKEYS +/datum/gear/fluff/ruda_badge + path = /obj/item/clothing/accessory/badge/holo/detective/ruda + display_name = "Ruda's Detective Badge" + ckeywhitelist = list("interrolouis") + character_name = list("Ruda Lizden") + +/datum/gear/fluff/kai_modkit + path = /obj/item/borg/upgrade/modkit/chassis_mod/kai + display_name = "Kai's Modkit" + ckeywhitelist = list("interrolouis") + character_name = list("Kai Highlands") + +/datum/gear/fluff/ivy_backpack + path = /obj/item/weapon/storage/backpack/messenger/sec/fluff/ivymoomoo + display_name = "Ivy's Backpack" + ckeywhitelist = list("ivymoomoo") + character_name = list("Ivy Baladeva") + +// J CKEYS +/datum/gear/fluff/mor_box + path = /obj/item/weapon/storage/box/fluff/morxaina + display_name = "Mor's Box" + ckeywhitelist = list("jacknoir413") + character_name = list("Mor Xaina") + +/datum/gear/fluff/areax_staff + path = /obj/item/weapon/storage/backpack/fluff/stunstaff + display_name = "Areax's Stun Staff" + ckeywhitelist = list("jacknoir413") + character_name = list("Areax Third") + allowed_roles = list("Security Officer, Warden, Head of Security") + +/datum/gear/fluff/earthen_uniform + path = /obj/item/clothing/under/fluff/earthenbreath + display_name = "Earthen's Uniform" + ckeywhitelist = list("jacobdragon") + character_name = list("Earthen Breath") + +/datum/gear/fluff/earthen_hairpin + path = /obj/item/clothing/head/fluff/hairflowerpin + display_name = "Earthen's Flower Pin" + ckeywhitelist = list("jacobdragon") + character_name = list("Earthen Breath") + +/datum/gear/fluff/cirra_box + path = /obj/item/weapon/storage/box/fluff/cirra + display_name = "Cirra's Box" + ckeywhitelist = list("jemli") + character_name = list("Cirra Mayhem") + +/datum/gear/fluff/jemli_fedora + path = /obj/item/clothing/head/fedora/fluff/jemli + display_name = "Jemli's Fedora" + ckeywhitelist = list("jemli") + character_name = list("Jemli") + +/datum/gear/fluff/jeremiah_permit + path = /obj/item/clothing/accessory/permit/gun/fluff/ace + display_name = "Ace's Gun Permit" + ckeywhitelist = list("jertheace") + character_name = list("Jeremiah Acacius") + allowed_roles = list("Colony Director", "Warden", "Head of Security") + +/datum/gear/fluff/jeremiah_gun + path = /obj/item/weapon/gun/projectile/p92x/large/preban/hp + display_name = "Ace's Gun" + ckeywhitelist = list("jertheace") + character_name = list("Jeremiah Acacius") + allowed_roles = list("Colony Director", "Warden", "Head of Security") + +/datum/gear/fluff/jeremiah_ammo + path = /obj/item/ammo_magazine/m9mm/large/preban/hp //Spare ammo + display_name = "Ace's Spare Ammo" + ckeywhitelist = list("jertheace") + character_name = list("Jeremiah Acacius") + allowed_roles = list("Colony Director", "Warden", "Head of Security") + +/datum/gear/fluff/jeremiah_holster + path = /obj/item/clothing/accessory/holster/armpit + display_name = "Ace's Holster" + ckeywhitelist = list("jertheace") + character_name = list("Jeremiah Acacius") + allowed_roles = list("Colony Director", "Warden", "Head of Security") + +/datum/gear/fluff/jeremiah_boots + path = /obj/item/clothing/shoes/boots/combat + display_name = "Ace's Boots" + ckeywhitelist = list("jertheace") + character_name = list("Jeremiah Acacius") + +/datum/gear/fluff/jeremiah_combatuniform + path = /obj/item/clothing/under/syndicate/combat + display_name = "Ace's Combat Uniform" + ckeywhitelist = list("jertheace") + character_name = list("Jeremiah Acacius") + +/datum/gear/fluff/joan_backpack + path = /obj/item/weapon/storage/backpack/dufflebag/sec/fluff/joanrisu + display_name = "Joan's backpack" + ckeywhitelist = list("joanrisu") + character_name = list("Joan Risu") + + +/datum/gear/fluff/katarina_backpack + path = /obj/item/weapon/storage/backpack/dufflebag/sec/fluff/katarina + display_name = "Katarina's Backpack" + ckeywhitelist = list("joanrisu") + character_name = list("Katarina Eine") + allowed_roles = list("Colony Director", "Warden", "Head of Security") + +/datum/gear/fluff/emoticon_box + path = /obj/item/weapon/storage/box/fluff/emoticon + display_name = "Emoticon's Box" + ckeywhitelist = list("joey4298") + character_name = list("Emoticon") + +/datum/gear/fluff/emoticon_mimeuniform + path = /obj/item/clothing/under/sexymime + display_name = "Emoticon's Mime Uniform" + ckeywhitelist = list("joey4298") + character_name = list("Emoticon") + +/datum/gear/fluff/emoticon_mimemask + path = /obj/item/clothing/mask/gas/sexymime + display_name = "Emoticon's Mime Mask" + ckeywhitelist = list("joey4298") + character_name = list("Emoticon") + +/datum/gear/fluff/harmony_medal + path = /obj/item/clothing/accessory/medal/gold/heroism + display_name = "Harmony's Heroism Medal" + ckeywhitelist = list("john.wayne9392") + character_name = list("Harmony Pretchl") + +/datum/gear/fluff/harmony_modkit + path = /obj/item/device/modkit_conversion/fluff/harmonysuit + display_name = "Harmony's Modkit" + ckeywhitelist = list("john.wayne9392") + character_name = list("Harmony Pretchl") + +/datum/gear/fluff/harmony_spacemodkit + path = /obj/item/device/modkit_conversion/fluff/harmonyspace + display_name = "Harmony's Modkit 2" + ckeywhitelist = list("john.wayne9392") + character_name = list("Harmony Pretchl") + +/datum/gear/fluff/koyo_box + path = /obj/item/weapon/storage/box/fluff/koyoakimomi + display_name = "Koyo's Box" + ckeywhitelist = list("jwguy") + character_name = list("Koyo Akimomi") + +// K CKEYS +/datum/gear/fluff/smu_medal + path = /obj/item/clothing/accessory/medal/nobel_science + display_name = "SMU's Nobel Science Award" + ckeywhitelist = list("keekenox") + character_name = list("SMU-453") + +/datum/gear/fluff/ketrai_hat + path = /obj/item/clothing/head/fluff/ketrai + display_name = "Ketrai's Hat" + ckeywhitelist = list("ketrai") + character_name = list("Ketrai") + +/datum/gear/fluff/amaya_id + path = /obj/item/weapon/card/id/fluff/amaya + display_name = "Amaya's ID" + ckeywhitelist = list("kiraalitruss") + character_name = list("Amaya Rahl") + +/datum/gear/fluff/kisuke_glasses + path = /obj/item/clothing/glasses/omnihud/kamina + display_name = "Kisuke's Kamina Glasses" + ckeywhitelist = list("kisukegema") + character_name = list("Kisuke Gema") + +/datum/gear/fluff/lassara_sheath + path = /obj/item/clothing/accessory/storage/knifeharness + display_name = "Lassara's Knife Harness" + ckeywhitelist = list("killjaden") + character_name = list("Lassara Faaira'Nrezi") + +/datum/gear/fluff/rana_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Rana's Unity Medal" + ckeywhitelist = list("kitchifox") + character_name = list("Rana Uma") + +/datum/gear/fluff/taiga_uniform + path = /obj/item/clothing/under/fluff/taiga + display_name = "Taifa's Uniform" + ckeywhitelist = list("kiwidaninja") + character_name = list("Chakat Taiga") + +/datum/gear/fluff/rischi_implant + path = /obj/item/weapon/implanter/reagent_generator/rischi + display_name = "Rischi's Implant" + ckeywhitelist = list("konabird") + character_name = list("Rischi") + +/datum/gear/fluff/ashley_medal + path = /obj/item/clothing/accessory/medal/nobel_science/fluff/ashley + display_name = "Ashley's Medal" + ckeywhitelist = list("knightfall5") + character_name = list("Ashley Kifer") + +// L CKEYS +/datum/gear/fluff/kenzie_hypospray + path = /obj/item/weapon/reagent_containers/hypospray/vial/kenzie + display_name = "Kenzie's Hypospray" + ckeywhitelist = list("lm40") + character_name = list("Kenzie Houser") + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") + +/datum/gear/fluff/brianna_backpack + path = /obj/item/weapon/storage/backpack/messenger/black/fluff/briana + display_name = "Briana's Backpack" + ckeywhitelist = list("luminescentring") + character_name = list("Briana Moore") + +/datum/gear/fluff/savannah_implant + path = /obj/item/weapon/implanter/reagent_generator/savannah + display_name = "Savannah's Implant" + ckeywhitelist = list("lycanthorph") + character_name = list("Savannah Dixon") + +// M CKEYS +/datum/gear/fluff/phi_box + path = /obj/item/weapon/storage/box/fluff/phi + display_name = "Phi's Box" + ckeywhitelist = list("mewchild") + character_name = list("Phi Vietsi") + +/datum/gear/fluff/giliana_labcoat + path = /obj/item/clothing/suit/storage/toggle/labcoat/fluff/molenar + display_name = "Giliana's Labcoat" + ckeywhitelist = list("molenar") + character_name = list("Giliana Gamish") + +/datum/gear/fluff/myryan_belt + path = /obj/item/weapon/storage/belt/utility/fluff/vulpine + display_name = "Myryan's Belt" + ckeywhitelist = list("myryan") + character_name = list("Myryan Karnage-Cunningham") + +/datum/gear/fluff/resh_uniform + path = /obj/item/clothing/suit/security/navyhos + display_name = "Resh's HoS Uniform" + ckeywhitelist = list("mrsignmeup") + character_name = list("Reshskakskakss Seekiseekis") + allowed_roles = list("Head of Security") + +/datum/gear/fluff/daniel_medal + path = /obj/item/clothing/accessory/medal/conduct + display_name = "Daniel's Conduct Medal" + ckeywhitelist = list("mrsignmeup") + character_name = list("Daniel Fisher") + +// N CKEYS +/datum/gear/fluff/awen_hat + path = /obj/item/clothing/head/fluff/wolfgirl + display_name = "Awen's Hat" + ckeywhitelist = list("natje") + character_name = list("Awen Henry") + +/datum/gear/fluff/awen_shoes + path = /obj/item/clothing/shoes/fluff/wolfgirl + ckeywhitelist = list("natje") + character_name = list("Awen Henry") + +/datum/gear/fluff/awen_uniform + path = /obj/item/clothing/under/fluff/wolfgirl + display_name = "Awen's Uniform" + ckeywhitelist = list("natje") + character_name = list("Awen Henry") + +/datum/gear/fluff/pumila_vines + path = /obj/item/clothing/under/fluff/aluranevines + display_name = "Pumila's Vines" + ckeywhitelist = list("natje") + character_name = list("Pumila") + +/datum/gear/fluff/annie_sweater + path = /obj/item/clothing/accessory/sweater/fluff/annie + display_name = "Annie's Sweater" + ckeywhitelist = list("nepox") + character_name = list("Annie Rose") + +// O CKEYS +/datum/gear/fluff/richard_chain + path = /obj/item/weapon/melee/fluff/holochain + display_name = "Richard's Holochain" + ckeywhitelist = list("orbisa") + character_name = list("Richard D'angelo") + +// P CKEYS +/datum/gear/fluff/lily_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Lily's Unity Medal" + ckeywhitelist = list("phoaly") + character_name = list("Lily Maximus") + +/datum/gear/fluff/lucuis_battery + path = /obj/item/weapon/fluff/dragor_dot + display_name = "Lucuis' Spare Battery" + ckeywhitelist = list("pontifexminimus") + character_name = list("Lucius Null") + +/datum/gear/fluff/lucia_battery + path = /obj/item/weapon/fluff/dragor_dot + display_name = "Lucia's Spare Battery" + ckeywhitelist = list("pontifexminimus") + character_name = list("Lucia Null") + +// Q CKEYS + +// R CKEYS +/datum/gear/fluff/tiemli_weldinggoggles + path = /obj/item/clothing/glasses/welding/tiemgogs + display_name = "Tiemli's Welding Goggles" + ckeywhitelist = list("radiantaurora") + character_name = list("Tiemli Kroto") + allowed_roles = list("Roboticist") + +// S CKEYS +/datum/gear/fluff/kateryna_voidsuit + path = /obj/item/clothing/suit/space/void/engineering/kate + display_name = "Kateryna's Voidsuit" + ckeywhitelist = list("samanthafyre") + character_name = list("Kateryna Petrovitch") + allowed_roles = list("Station Engineer", "Chief Engineer", "Atmospheric Technician") + +/datum/gear/fluff/katerina_spacesuit + path = /obj/item/clothing/head/helmet/space/fluff/kate + display_name = "Kateryna's Helmet" + ckeywhitelist = list("samanthafyre") + character_name = list("Kateryna Petrovitch") + allowed_roles = list("Station Engineer", "Chief Engineer", "Atmospheric Technician") + +/datum/gear/fluff/kateryna_armorvest + path = /obj/item/clothing/suit/armor/vest/wolftaur/kate + display_name = "Kateryna's Armor Vest" + ckeywhitelist = list("samanthafyre") + character_name = list("Kateryna Petrovitch") + allowed_roles = list("Security Officer", "Warden", "Head of Security", "Colony Director", "Head of Personnel") + +/datum/gear/fluff/viktor_flask + path = /obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask/fluff/viktor + display_name = "Viktor's Flask" + ckeywhitelist = list("semaun") + character_name = list("Viktor Solothurn") + +/datum/gear/fluff/scree_modkit + path = /obj/item/device/modkit_conversion/fluff/screekit + display_name = "Scree's Modkit" + ckeywhitelist = list("scree") + character_name = list("Scree") + +/datum/gear/fluff/scree_pompom + path = /obj/item/clothing/head/fluff/pompom + display_name = "Scree's Weird PopPom thing" + ckeywhitelist = list("scree") + character_name = list("Scree") + +/datum/gear/fluff/alfonso_sunglasses + path = /obj/item/clothing/glasses/sunglasses/fluff/alfonso + display_name = "Alfonso's Sunglasses" + ckeywhitelist = list("seiga") + character_name = list("Alfonso Oak Telanor") + +/datum/gear/fluff/nthasd_modkit //Converts a Security suit's sprite + path = /obj/item/device/modkit_conversion/hasd + display_name = "NT-HASD #556's Modkit" + ckeywhitelist = list("silencedmp5a5") + character_name = list("NT-HASD #556") + allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective") + +/datum/gear/fluff/tasy_clownuniform + path = /obj/item/clothing/under/sexyclown + display_name = "Tasy's Clown Uniform" + ckeywhitelist = list("silvertalismen") + character_name = list("Tasy Ruffles") + +/datum/gear/fluff/tasy_clownmask + path = /obj/item/clothing/mask/gas/sexyclown + display_name = "Tasy's Clownmask" + ckeywhitelist = list("silvertalismen") + character_name = list("Tasy Ruffles") + +/datum/gear/fluff/tasy_clownPDA + path = /obj/item/device/pda/clown + display_name = "Tasy's Clown PDA" + ckeywhitelist = list("silvertalismen") + character_name = list("Tasy Ruffles") + +/datum/gear/fluff/evian_implant + path = /obj/item/weapon/implanter/reagent_generator/evian + display_name = "Evian's Implant" + ckeywhitelist = list("silvertalismen") + character_name = list("Evian") + +/datum/gear/fluff/fortune_backpack + path = /obj/item/weapon/storage/backpack/satchel/fluff/swat43bag + display_name = "Fortune's Backpack" + ckeywhitelist = list("swat43") + character_name = list("Fortune Bloise") + +/datum/gear/fluff/alexis_cane + path = /obj/item/weapon/cane/wand + display_name = "Alexis' Cane" + ckeywhitelist = list("stobarico") + character_name = list("Alexis Bloise") + +/datum/gear/fluff/roiz_implant + path = /obj/item/weapon/implanter/reagent_generator/roiz + display_name = "Roiz's Implant" + ckeywhitelist = list("spoopylizz") + character_name = list("Roiz Lizden") + +/datum/gear/fluff/roiz_coat + path = /obj/item/clothing/suit/storage/hooded/wintercoat/roiz + display_name = "Roiz's Coat" + ckeywhitelist = list("spoopylizz") + character_name = list("Roiz Lizden") + +/datum/gear/fluff/silent_mimemask + path = /obj/item/clothing/mask/gas/sexymime + display_name = "Silent Stripe's Mime Mask" + ckeywhitelist = list("suicidalpickles") + character_name = list("Silent Stripes") + +/datum/gear/fluff/silent_mimeuniform + path = /obj/item/clothing/under/sexymime + display_name = "Silent Stripe's Mime Uniform" + ckeywhitelist = list("suicidalpickles") + character_name = list("Silent Stripes") + +// T CKEYS +/datum/gear/fluff/ascian_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Ascian's Unity Medal" + ckeywhitelist = list("tabiranth") + character_name = list("Ascian") + +/datum/gear/fluff/ascian_spiritspawner + path = /obj/item/weapon/grenade/spawnergrenade/spirit + display_name = "The Best Kitten" + ckeywhitelist = list("tabiranth") + character_name = list("Ascian") + +/datum/gear/fluff/ascian_shelterpod + path = /obj/item/device/survivalcapsule/tabiranth + display_name = "Ascian's Shelterpod" + ckeywhitelist = list("tabiranth") + character_name = list("Ascian") + +/datum/gear/fluff/lasshseeki_ealimplant + path = /obj/item/weapon/implant/language/eal + display_name = "Lasshseeki's EAL Implant" + ckeywhitelist = list("techtypes") + character_name = list("Lasshseeki Korss") + +/datum/gear/fluff/konor_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Konor's Unity Medal" + ckeywhitelist = list("tinydude16") + character_name = list("Konor Foxe") + +// U CKEYS + +// V CKEYS +/datum/gear/fluff/vakashi_permit + path = /obj/item/clothing/accessory/permit/gun/fluff/Vakashi + display_name = "Vakashi's Pepperspray Permit" + ckeywhitelist = list("vailthewolf") + character_name = list("Vakashi") + +/datum/gear/fluff/vakashi_pepperspray + path = /obj/item/weapon/reagent_containers/spray/pepper + display_name = "Vakashi's Pepperspray" + ckeywhitelist = list("vailthewolf") + character_name = list("Vakashi") + +/datum/gear/fluff/cameron_glasses + path = /obj/item/clothing/glasses/fluff/science_proper + display_name = "Cameron's Science Glasses" + ckeywhitelist = list("verkister") + character_name = list("Cameron Eggbert") + +/datum/gear/fluff/cameron_disk + path = /obj/item/weapon/disk/limb/eggnerdltd + display_name = "Cameron's Eggnerd Disk" + ckeywhitelist = list("verkister") + character_name = list("Cameron Eggbert") + +/datum/gear/fluff/opie_glasses + path = /obj/item/clothing/glasses/fluff/spiffygogs + display_name = "Opie's Goggles" + ckeywhitelist = list("verkister") + character_name = list("Opie Eggbert") + +/datum/gear/fluff/verin_hazardvest + path = /obj/item/clothing/suit/storage/hazardvest/fluff/verin + display_name = "Verin's Hazard Vest" + ckeywhitelist = list("virgo113") + character_name = list("Verin Raharra") + +/datum/gear/fluff/lucina_pda + path = /obj/item/device/pda/heads/cmo/fluff/lucinapda + display_name = "Lucina's PDA" + ckeywhitelist = list("vorrarkul") + character_name = list("Lucina Dakarim") + +/datum/gear/fluff/lucina_medal + path = /obj/item/clothing/accessory/medal/gold/fluff/lucina + display_name = "Lucina's Gold Medal" + ckeywhitelist = list("vorrarkul") + character_name = list("Lucina Dakarim") + +/datum/gear/fluff/lucina_dress + path = /obj/item/clothing/under/dress/fluff/lucinadress + display_name = "Lucina's Dress" + ckeywhitelist = list("vorrarkul") + character_name = list("Lucina Dakarim") + +/datum/gear/fluff/melanie_skeleton + path = /obj/item/clothing/under/fluff/slime_skeleton + display_name = "Melanie's Skeleton" + ckeywhitelist = list("vorrarkul") + character_name = list("Melanie Farmer") + +/datum/gear/fluff/nyssa_coat + path = /obj/item/clothing/suit/storage/hooded/wintercoat/cargo + display_name = "Nyssa's Coat" + ckeywhitelist = list("vorrarkul") + character_name = list("Nyssa Brennan") + +/datum/gear/fluff/theodora_suit + path = /obj/item/clothing/suit/chococoat + display_name = "Theodora's Coat" + ckeywhitelist = list("vorrarkul") + character_name = list("Theodora Lindt") + +/datum/gear/fluff/theodora_implant + path = /obj/item/weapon/implanter/reagent_generator/vorrarkul + display_name = "Theodora's Implant" + ckeywhitelist = list("vorrarkul") + character_name = list("Theodora Lindt") + +/datum/gear/fluff/kaitlyn_plush + path = /obj/item/toy/plushie/mouse/fluff + display_name = "Kaitlyn's Mouse Plush" + ckeywhitelist = list("vorrarkul") + character_name = list("Kaitlyn Fiasco") + +/datum/gear/fluff/keturah_maiddress + path = /obj/item/clothing/under/dress/maid/ + display_name = "Keturah's Maid Dress" + ckeywhitelist = list("viveret") + character_name = list("Keturah") + +/datum/gear/fluff/silentio_mimeuniform + path = /obj/item/clothing/under/sexymime + display_name = "Silentio's Mime Uniform" + ckeywhitelist = list("viveret") + character_name = list("Silentio") + +/datum/gear/fluff/silentio_mimemask + path = /obj/item/clothing/mask/gas/sexymime + display_name = "Silentio's Mime Mask" + ckeywhitelist = list("Viveret") + character_name = list("Silentio") + +// W CKEYS +/datum/gear/fluff/sthasha_bracer + path = /obj/item/clothing/accessory/bracer/fluff/xander_sthasha + display_name = "S'thasha's Bracer" + ckeywhitelist = list("wanderingdeviant") + character_name = list("S'thasha Tavakdavi") + +/datum/gear/fluff/silas_glasses + path = /obj/item/clothing/glasses/threedglasses + display_name = "Silas' 3-D Glasses" + ckeywhitelist = list("werebear") + character_name = list("Silas Newton") + +/datum/gear/fluff/vinjj_weldingmask + path = /obj/item/clothing/head/welding/fluff/vinjj + display_name = "Vinjj's Welding Mask" + ckeywhitelist = list("whiskyrose") + character_name = list("Vinjj") + +/datum/gear/fluff/tempest_hudglases + path = /obj/item/clothing/glasses/omnihud/med/fluff/wickedtemphud + display_name = "Tempest's Medical Hud" + ckeywhitelist = list("wickedtemp") + character_name = list("Chakat Tempest Venesare") + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") + +/datum/gear/fluff/tempest_hypospray + path = /obj/item/weapon/reagent_containers/hypospray/vial/tempest + display_name = "Tempest's Hypospray" + ckeywhitelist = list("wickedtemp") + character_name = list("Chakat Tempest Venesare") + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") + +/datum/gear/fluff/tempest_backpack + path = /obj/item/weapon/storage/backpack/saddlebag/tempest + display_name = "Tempest's Saddlebag" + ckeywhitelist = list("wickedtemp") + character_name = list("Chakat Tempest Venesare") + +/datum/gear/fluff/tempest_implant + path = /obj/item/weapon/implanter/reagent_generator/tempest + display_name = "Tempest's Implant" + ckeywhitelist = list("wickedtemp") + character_name = list("Chakat Tempest Venesare") + +// X CKEYS +/datum/gear/fluff/penelope_box + path = /obj/item/weapon/storage/box/fluff/penelope + display_name = "Penelope's Box" + ckeywhitelist = list("xsdew") + character_name = list("Penelope Allen") + +/datum/gear/fluff/ali_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Ali's Unity Medal" + ckeywhitelist = list("xonkon") + character_name = list("Ali") + +// Y CKEYS + +// Z CKEYS +/datum/gear/fluff/tachika_medal + path = /obj/item/clothing/accessory/medal/conduct + display_name = "Tachika's Conduct Medal" + ckeywhitelist = list("zammyman") + character_name = list("Tachika") + +/datum/gear/fluff/zaoozaoo_hat + path = /obj/item/clothing/head/fluff/zao + display_name = "Zaoozaoo's Hat" + ckeywhitelist = list("zigfe") + character_name = list("Zaoozaoo Xrimxuqmqixzix") + +/datum/gear/fluff/nehi_radio + path = /obj/item/device/radio/headset/fluff/zodiacshadow + display_name = "Nehi's Radio" + ckeywhitelist = list("zodiacshadow") + character_name = list("Nehi Maximus") diff --git a/code/modules/client/preference_setup/loadout/loadout_smoking.dm b/code/modules/client/preference_setup/loadout/loadout_smoking.dm index cbedca3a67..96db8bf2fe 100644 --- a/code/modules/client/preference_setup/loadout/loadout_smoking.dm +++ b/code/modules/client/preference_setup/loadout/loadout_smoking.dm @@ -27,6 +27,8 @@ ..() var/list/zippos = list() for(var/zippo in typesof(/obj/item/weapon/flame/lighter/zippo)) + if(zippo in typesof(/obj/item/weapon/flame/lighter/zippo/fluff)) //VOREStation addition + continue //VOREStation addition var/obj/item/weapon/flame/lighter/zippo/zippo_type = zippo zippos[initial(zippo_type.name)] = zippo_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(zippos)) diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index e2a661f5f3..1802d2c009 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -106,6 +106,8 @@ datum/gear/suit/duster ..() var/list/hazards = list() for(var/hazard_style in typesof(/obj/item/clothing/suit/storage/hazardvest)) + if(hazard_style in typesof(/obj/item/clothing/suit/storage/hazardvest/fluff)) //VOREStation addition + continue //VOREStation addition var/obj/item/clothing/suit/storage/hazardvest/hazardvest = hazard_style hazards[initial(hazardvest.name)] = hazardvest gear_tweaks += new/datum/gear_tweak/path(sortAssoc(hazards)) @@ -493,3 +495,11 @@ datum/gear/suit/duster display_name = "snowsuit, supply" path = /obj/item/clothing/suit/storage/snowsuit/cargo allowed_roles = list("Quartermaster","Shaft Miner","Cargo Technician","Head of Personnel") + +/datum/gear/suit/miscellaneous/cardigan + display_name = "cardigan" + path = /obj/item/clothing/suit/storage/toggle/cardigan + +/datum/gear/suit/miscellaneous/cardigan/New() + ..() + gear_tweaks = list(gear_tweak_free_color_choice) \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm index 719ba98a38..e7bb0493f3 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm @@ -1,9 +1,3 @@ -/datum/gear/suit/roles/poncho/cloak/research - allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist", "Explorer", "Pathfinder") - -/datum/gear/suit/roles/poncho/cloak/medical - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") - /datum/gear/suit/wintercoat/medical allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform.dm b/code/modules/client/preference_setup/loadout/loadout_uniform.dm index 868145b33f..7de598fc83 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform.dm @@ -65,6 +65,8 @@ ..() var/list/skirts = list() for(var/skirt in (typesof(/obj/item/clothing/under/skirt))) + if(skirt in typesof(/obj/item/clothing/under/skirt/fluff)) //VOREStation addition + continue //VOREStation addition var/obj/item/clothing/under/skirt/skirt_type = skirt skirts[initial(skirt_type.name)] = skirt_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(skirts)) @@ -223,6 +225,8 @@ ..() var/list/msuits = list() for(var/msuit in typesof(/obj/item/clothing/under/suit_jacket)) + if(msuit in typesof(/obj/item/clothing/under/suit_jacket/female/fluff)) //VOREStation addition + continue //VOREStation addition var/obj/item/clothing/suit/msuit_type = msuit msuits[initial(msuit_type.name)] = msuit_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(msuits)) @@ -502,3 +506,14 @@ display_name = "plain ascetic garb" path = /obj/item/clothing/under/ascetic +/datum/gear/uniform/pleated + display_name = "pleated skirt" + path = /obj/item/clothing/under/skirt/pleated + +/datum/gear/uniform/pleated/New() + ..() + gear_tweaks = list(gear_tweak_free_color_choice) + +/datum/gear/uniform/lilacdress + display_name = "lilac dress" + path = /obj/item/clothing/under/dress/lilacdress diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm b/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm index 1508905c60..be65b874a6 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm @@ -3,9 +3,9 @@ path = /obj/item/clothing/under/permit //Polaris overrides -/datum/gear/uniform/pt/sifguard +/datum/gear/uniform/solgov/pt/sifguard display_name = "pt uniform, planetside sec" - path = /obj/item/clothing/under/pt/sifguard + path = /obj/item/clothing/under/solgov/pt/sifguard //KHI Uniforms /datum/gear/uniform/job_khi/cmd @@ -168,4 +168,8 @@ Swimsuits //Tron Siren outfit /datum/gear/uniform/siren display_name = "jumpsuit, Siren" - path = /obj/item/clothing/under/fluff/siren \ No newline at end of file + path = /obj/item/clothing/under/fluff/siren + +/datum/gear/uniform/suit/v_nanovest + display_name = "Varmacorp nanovest" + path = /obj/item/clothing/under/fluff/v_nanovest \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_utility.dm b/code/modules/client/preference_setup/loadout/loadout_utility.dm index 52fce45031..44e0bc10ff 100644 --- a/code/modules/client/preference_setup/loadout/loadout_utility.dm +++ b/code/modules/client/preference_setup/loadout/loadout_utility.dm @@ -123,7 +123,25 @@ display_name = "implant, tracking" path = /obj/item/weapon/implant/tracking/weak cost = 0 //VOREStation Edit. Changed cost to 0 -/* VOREStation Edit - Make languages great again + +/datum/gear/utility/implant/neural + display_name = "implant, neural assistance web" + description = "A complex web implanted into the subject, medically in order to compensate for neurological disease." + path = /obj/item/weapon/implant/neural/roundstart + cost = 6 + +/datum/gear/utility/implant/dud1 + display_name = "implant, head" + description = "An implant with no obvious purpose." + path = /obj/item/weapon/implant/dud + cost = 1 + +/datum/gear/utility/implant/dud2 + display_name = "implant, torso" + description = "An implant with no obvious purpose." + path = /obj/item/weapon/implant/dud/torso + cost = 1 + /datum/gear/utility/implant/language cost = 2 exploitable = 0 @@ -137,7 +155,7 @@ display_name = "vocal synthesizer, Skrellian" description = "A surgically implanted vocal synthesizer which allows the owner to speak Common Skrellian, if they know it." path = /obj/item/weapon/implant/language/skrellian -*/ + /datum/gear/utility/pen display_name = "Fountain Pen" path = /obj/item/weapon/pen/fountain @@ -159,3 +177,48 @@ /datum/gear/utility/umbrella/New() ..() gear_tweaks = list(gear_tweak_free_color_choice) + +/**************** +modular computers +****************/ + +/datum/gear/utility/cheaptablet + display_name = "tablet computer: cheap" + display_name = "tablet computer, cheap" + path = /obj/item/modular_computer/tablet/preset/custom_loadout/cheap + cost = 3 + +/datum/gear/utility/normaltablet + display_name = "tablet computer: advanced" + display_name = "tablet computer, advanced" + path = /obj/item/modular_computer/tablet/preset/custom_loadout/advanced + cost = 4 + +/datum/gear/utility/customtablet + display_name = "tablet computer: custom" + display_name = "tablet computer, custom" + path = /obj/item/modular_computer/tablet + cost = 4 + +/datum/gear/utility/customtablet/New() + ..() + gear_tweaks += new /datum/gear_tweak/tablet() + +/datum/gear/utility/cheaplaptop + display_name = "laptop computer, cheap" + path = /obj/item/modular_computer/laptop/preset/custom_loadout/cheap + cost = 4 + +/datum/gear/utility/normallaptop + display_name = "laptop computer, advanced" + path = /obj/item/modular_computer/laptop/preset/custom_loadout/advanced + cost = 5 + +/datum/gear/utility/customlaptop + display_name = "laptop computer, custom" + path = /obj/item/modular_computer/laptop/preset/ + cost = 6 //VOREStation Edit + +/datum/gear/utility/customlaptop/New() + ..() + gear_tweaks += new /datum/gear_tweak/laptop() diff --git a/code/modules/client/preference_setup/loadout/loadout_vr.dm b/code/modules/client/preference_setup/loadout/loadout_vr.dm new file mode 100644 index 0000000000..1f7a31311e --- /dev/null +++ b/code/modules/client/preference_setup/loadout/loadout_vr.dm @@ -0,0 +1,3 @@ +/datum/gear + var/list/ckeywhitelist + var/list/character_name \ No newline at end of file diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 70381ee123..ca2bc4d787 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -54,7 +54,7 @@ if(alt_title && !(alt_title in job.alt_titles)) pref.player_alt_titles -= job.title -/datum/category_item/player_setup_item/occupation/content(mob/user, limit = 19, list/splitJobs = list("Chief Engineer")) //VOREStation Edit +/datum/category_item/player_setup_item/occupation/content(mob/user, limit = 20, list/splitJobs = list("Pathfinder")) //VOREStation Edit if(!job_master) return diff --git a/code/modules/client/preference_setup/traits/trait_defines.dm b/code/modules/client/preference_setup/traits/trait_defines.dm index aa53392f2c..8ad3ff9253 100644 --- a/code/modules/client/preference_setup/traits/trait_defines.dm +++ b/code/modules/client/preference_setup/traits/trait_defines.dm @@ -72,25 +72,45 @@ Regardless, you find it quite difficult to land shots where you wanted them to go." modifier_type = /datum/modifier/trait/inaccurate -/datum/trait/modifier/physical/smaller - name = "Smaller" - modifier_type = /datum/modifier/trait/smaller - mutually_exclusive = list(/datum/trait/modifier/physical/small, /datum/trait/modifier/physical/large, /datum/trait/modifier/physical/larger) +/datum/trait/modifier/physical/shorter + name = "Shorter" + modifier_type = /datum/modifier/trait/shorter + mutually_exclusive = list(/datum/trait/modifier/physical/short, /datum/trait/modifier/physical/tall, /datum/trait/modifier/physical/taller) -/datum/trait/modifier/physical/small - name = "Small" - modifier_type = /datum/modifier/trait/small - mutually_exclusive = list(/datum/trait/modifier/physical/smaller, /datum/trait/modifier/physical/large, /datum/trait/modifier/physical/larger) +/datum/trait/modifier/physical/short + name = "Short" + modifier_type = /datum/modifier/trait/short + mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/tall, /datum/trait/modifier/physical/taller) -/datum/trait/modifier/physical/large - name = "Large" - modifier_type = /datum/modifier/trait/large - mutually_exclusive = list(/datum/trait/modifier/physical/smaller, /datum/trait/modifier/physical/small, /datum/trait/modifier/physical/larger) +/datum/trait/modifier/physical/tall + name = "Tall" + modifier_type = /datum/modifier/trait/tall + mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/short, /datum/trait/modifier/physical/taller) -/datum/trait/modifier/physical/larger - name = "Larger" - modifier_type = /datum/modifier/trait/larger - mutually_exclusive = list(/datum/trait/modifier/physical/smaller, /datum/trait/modifier/physical/small, /datum/trait/modifier/physical/large) +/datum/trait/modifier/physical/taller + name = "Taller" + modifier_type = /datum/modifier/trait/taller + mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/short, /datum/trait/modifier/physical/tall) + +/datum/trait/modifier/physical/thin + name = "Thin" + modifier_type = /datum/modifier/trait/thin + mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thinner) + +/datum/trait/modifier/physical/thinner + name = "Rail Thin" + modifier_type = /datum/modifier/trait/thinner + mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thin) + +/datum/trait/modifier/physical/fat + name = "Broad-Shouldered" + modifier_type = /datum/modifier/trait/fat + mutually_exclusive = list(/datum/trait/modifier/physical/thin, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thinner) + +/datum/trait/modifier/physical/obese + name = "Heavily Built" + modifier_type = /datum/modifier/trait/obese + mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/thinner, /datum/trait/modifier/physical/thin) /datum/trait/modifier/physical/colorblind_protanopia name = "Protanopia" diff --git a/code/modules/client/preference_setup/vore/06_vantag.dm b/code/modules/client/preference_setup/vore/06_vantag.dm index 65bc66a495..c65c379bf2 100644 --- a/code/modules/client/preference_setup/vore/06_vantag.dm +++ b/code/modules/client/preference_setup/vore/06_vantag.dm @@ -27,7 +27,7 @@ /datum/category_item/player_setup_item/vore/vantag/content(var/mob/user) . += "
      " - . += "Event Volunteer:[pref.vantag_volunteer ? "Yes" : "No"]
      " + . += "Event Volunteer:[pref.vantag_volunteer ? "Yes" : "No"]
      " . += "Event Pref:[vantag_choices_list[pref.vantag_preference]]
      " /datum/category_item/player_setup_item/vore/vantag/OnTopic(var/href, var/list/href_list, var/mob/user) diff --git a/code/modules/client/preference_setup/vore/07_traits.dm b/code/modules/client/preference_setup/vore/07_traits.dm index fe87d35fbe..06b748f359 100644 --- a/code/modules/client/preference_setup/vore/07_traits.dm +++ b/code/modules/client/preference_setup/vore/07_traits.dm @@ -96,7 +96,7 @@ log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet /datum/category_item/player_setup_item/vore/traits/content(var/mob/user) - . += "Custom Species " + . += "Custom Species Name: " . += "[pref.custom_species ? pref.custom_species : "-Input Name-"]
      " var/datum/species/selected_species = all_species[pref.species] diff --git a/code/modules/client/preference_setup/vore/09_misc.dm b/code/modules/client/preference_setup/vore/09_misc.dm index 09369d4901..2911c8a10d 100644 --- a/code/modules/client/preference_setup/vore/09_misc.dm +++ b/code/modules/client/preference_setup/vore/09_misc.dm @@ -1,5 +1,4 @@ -/datum/preferences - var/show_in_directory = TRUE +//TFF 5/8/19 - moved /datum/preferences to preferences_vr.dm /datum/category_item/player_setup_item/vore/misc name = "Misc Settings" @@ -7,20 +6,35 @@ /datum/category_item/player_setup_item/vore/misc/load_character(var/savefile/S) S["show_in_directory"] >> pref.show_in_directory - + S["sensorpref"] >> pref.sensorpref //TFF 5/8/19 - add sensor pref setting to load after saved /datum/category_item/player_setup_item/vore/misc/save_character(var/savefile/S) S["show_in_directory"] << pref.show_in_directory + S["sensorpref"] << pref.sensorpref //TFF 5/8/19 - add sensor pref setting to be saveable + +//TFF 5/8/19 - add new datum category to allow for setting multiple settings when this is selected in the loadout. +/datum/category_item/player_setup_item/vore/misc/copy_to_mob(var/mob/living/carbon/human/character) + if(pref.sensorpref > 5 || pref.sensorpref < 1) + pref.sensorpref = 5 + character.sensorpref = pref.sensorpref /datum/category_item/player_setup_item/vore/misc/sanitize_character() pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory)) + pref.sensorpref = sanitize_integer(pref.sensorpref, 1, sensorpreflist.len, initial(pref.sensorpref)) //TFF - 5/8/19 - add santisation for sensor prefs /datum/category_item/player_setup_item/vore/misc/content(var/mob/user) . += "
      " . += "Appear in Character Directory:[pref.show_in_directory ? "Yes" : "No"]
      " + . += "Suit Sensors Preference:[sensorpreflist[pref.sensorpref]]
      " //TFF 5/8/19 - Allow selection of sensor settings from off, binary, vitals, tracking, or random /datum/category_item/player_setup_item/vore/misc/OnTopic(var/href, var/list/href_list, var/mob/user) if(href_list["toggle_show_in_directory"]) pref.show_in_directory = pref.show_in_directory ? 0 : 1; return TOPIC_REFRESH + //TFF 5/8/19 - add new thing so you can choose the sensor setting your character can get. + else if(href_list["toggle_sensor_setting"]) + var/new_sensorpref = input(user, "Choose your character's sensor preferences:", "Character Preferences", sensorpreflist[pref.sensorpref]) as null|anything in sensorpreflist + if (!isnull(new_sensorpref) && CanUseTopic(user)) + pref.sensorpref = sensorpreflist.Find(new_sensorpref) + return TOPIC_REFRESH return ..(); diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm index 51412906d7..fa51b9e4e9 100644 --- a/code/modules/client/preferences_toggle_procs.dm +++ b/code/modules/client/preferences_toggle_procs.dm @@ -10,7 +10,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear all mob speech as a ghost.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TGEars") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -25,7 +25,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] see all emotes as a ghost.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TGVision") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -40,7 +40,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear all radios as a ghost.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TGRadio") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -55,7 +55,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear dead chat as a ghost.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TDeadChat") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -70,7 +70,7 @@ to_chat(src,"You will [ (is_preference_enabled(/datum/client_preference/show_ooc)) ? "now" : "no longer"] hear global out of character chat.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -85,7 +85,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear local out of character chat.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -100,7 +100,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] have the speech indicator.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TTIND") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -115,7 +115,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive noise from admin messages.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TAHelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -130,7 +130,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear music in the lobby.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TLobMusic") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -145,7 +145,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear MIDIs from admins.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TAMidis") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -160,7 +160,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear ambient noise.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TAmbience") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -175,7 +175,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear weather sounds.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TWeatherSounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -190,7 +190,7 @@ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear a hum from the supermatter.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TSupermatterHum") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -205,7 +205,7 @@ src << "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear jukebox music." - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TJukebox") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -218,7 +218,7 @@ if(!role_flag) return prefs.be_special ^= role_flag - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) to_chat(src,"You will [(prefs.be_special & role_flag) ? "now" : "no longer"] be considered for [role] events (where possible).") @@ -231,7 +231,7 @@ var/pref_path = /datum/client_preference/safefiring toggle_preference(pref_path) - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) to_chat(src,"You will now use [(is_preference_enabled(/datum/client_preference/safefiring)) ? "safe" : "dangerous"] firearms firing.") @@ -244,7 +244,7 @@ var/pref_path = /datum/client_preference/mob_tooltips toggle_preference(pref_path) - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) to_chat(src,"You will now [(is_preference_enabled(/datum/client_preference/mob_tooltips)) ? "see" : "not see"] mob tooltips.") @@ -257,7 +257,7 @@ var/pref_path = /datum/client_preference/instrument_toggle toggle_preference(pref_path) - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) to_chat(src, "You will now [(is_preference_enabled(/datum/client_preference/instrument_toggle)) ? "hear" : "not hear"] instruments being played.") @@ -276,7 +276,7 @@ if(check_rights(R_ADMIN|R_DEBUG)) toggle_preference(pref_path) to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive debug logs.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -291,6 +291,6 @@ if(check_rights(R_ADMIN|R_MOD)) toggle_preference(pref_path) to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive attack logs.") - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm index 23105324a5..5c8171ce27 100644 --- a/code/modules/client/preferences_vr.dm +++ b/code/modules/client/preferences_vr.dm @@ -1 +1,36 @@ -//File isn't currently being used. +//TFF 5/8/19 - minor refactoring of this thing from 09_misc.dm to call this for preferences. +datum/preferences + var/show_in_directory = 1 //TFF 5/8/19 - show in Character Directory + var/sensorpref = 5 //TFF 5/8/19 - set character's suit sensor level + +//Why weren't these in game toggles already? +/client/verb/toggle_eating_noises() + set name = "Eating Noises" + set category = "Preferences" + set desc = "Toggles Vore Eating noises." + + var/pref_path = /datum/client_preference/eating_noises + + toggle_preference(pref_path) + + src << "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear eating related vore noises." + + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TEatNoise") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/verb/toggle_digestion_noises() + set name = "Digestion Noises" + set category = "Preferences" + set desc = "Toggles Vore Digestion noises." + + var/pref_path = /datum/client_preference/digestion_noises + + toggle_preference(pref_path) + + src << "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear digestion related vore noises." + + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TDigestNoise") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm index 4a736aec5e..e7393e22c5 100644 --- a/code/modules/client/ui_style.dm +++ b/code/modules/client/ui_style.dm @@ -77,5 +77,5 @@ var/global/list/all_tooltip_styles = list( prefs.UI_style = UI_style_new prefs.UI_style_alpha = UI_style_alpha_new prefs.UI_style_color = UI_style_color_new - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) usr << "UI was saved" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 1676fc5b85..8af9f36d53 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -293,6 +293,17 @@ punch_force = initial(punch_force) wearer = null +/obj/item/clothing/gloves + var/datum/unarmed_attack/special_attack = null //do the gloves have a special unarmed attack? + var/special_attack_type = null + +/obj/item/clothing/gloves/New() + ..() + if(special_attack_type && ispath(special_attack_type)) + special_attack = new special_attack_type + + + ///////////////////////////////////////////////////////////////////// //Rings @@ -502,6 +513,7 @@ if(usr.put_in_hands(holding)) usr.visible_message("\The [usr] pulls a knife out of their boot!") holding = null + overlays -= image(icon, "[icon_state]_knife") else usr << "Your need an empty, unbroken hand to do that." holding.forceMove(src) @@ -546,14 +558,25 @@ update_icon() /obj/item/clothing/shoes/update_icon() - overlays.Cut() + overlays.Cut() //This removes all the overlays on the sprite and then goes down a checklist adding them as required. + if(blood_DNA) + add_blood() if(holding) overlays += image(icon, "[icon_state]_knife") + if(contaminated) + overlays += contamination_overlay + if(gurgled) //VOREStation Edit Start + decontaminate() + gurgle_contaminate() //VOREStation Edit End if(ismob(usr)) var/mob/M = usr M.update_inv_shoes() return ..() +/obj/item/clothing/shoes/clean_blood() + update_icon() + return ..() + /obj/item/clothing/shoes/proc/handle_movement(var/turf/walking, var/running) if(prob(1) && !recent_squish) //VOREStation edit begin recent_squish = 1 @@ -643,6 +666,7 @@ |ACCESSORY_SLOT_ARMBAND\ |ACCESSORY_SLOT_DECOR\ |ACCESSORY_SLOT_MEDAL\ + |ACCESSORY_SLOT_INSIGNIA\ |ACCESSORY_SLOT_TIE\ |ACCESSORY_SLOT_OVER) restricted_accessory_slots = (\ diff --git a/code/modules/clothing/clothing_vr.dm b/code/modules/clothing/clothing_vr.dm index fc092d48bc..f81dd53c18 100644 --- a/code/modules/clothing/clothing_vr.dm +++ b/code/modules/clothing/clothing_vr.dm @@ -4,6 +4,10 @@ /obj/item/clothing/shoes var/list/inside_emotes = list() var/recent_squish = 0 + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/shoes.dmi', + SPECIES_VOX = 'icons/mob/species/vox/shoes.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/feet.dmi') /obj/item/clothing/shoes/New() inside_emotes = list( @@ -51,6 +55,17 @@ ..() +/obj/item/clothing/gloves + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/gloves.dmi', + SPECIES_VOX = 'icons/mob/species/vox/gloves.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/hands.dmi') + +/obj/item/clothing/ears + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/ears.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/ears.dmi') + /obj/item/clothing/relaymove(var/mob/living/user,var/direction) if(recent_struggle) @@ -98,13 +113,18 @@ SPECIES_ZORREN_FLAT = 'icons/mob/species/fennec/mask_vr.dmi', SPECIES_AKULA = 'icons/mob/species/akula/mask_vr.dmi', SPECIES_VULPKANIN = 'icons/mob/species/vulpkanin/mask.dmi', - SPECIES_XENOCHIMERA = 'icons/mob/species/tajaran/mask_vr.dmi' + SPECIES_XENOCHIMERA = 'icons/mob/species/tajaran/mask_vr.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/masks.dmi' ) //"Spider" = 'icons/mob/species/spider/mask_vr.dmi' Add this later when they have custom mask sprites and everything. //Switch to taur sprites if a taur equips /obj/item/clothing/suit var/taurized = FALSE //Easier than trying to 'compare icons' to see if it's a taur suit + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/suit.dmi', + SPECIES_VOX = 'icons/mob/species/vox/suit.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/suit.dmi') /obj/item/clothing/suit/equipped(var/mob/user, var/slot) var/normalize = TRUE @@ -126,13 +146,38 @@ return ..() // Taur suits need to be shifted so its centered on their taur half. -/obj/item/clothing/suit/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer = 0) +/obj/item/clothing/suit/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer = 0,var/icon/clip_mask) var/image/standing = ..() if(taurized) //Special snowflake var on suits standing.pixel_x = -16 standing.layer = BODY_LAYER + 15 // 15 is above tail layer, so will not be covered by taurbody. return standing -//Underclothes +//TFF 5/8/19 - sets Vorestation /obj/item/clothing/under sensor setting default? /obj/item/clothing/under - species_restricted = list("exclude", SPECIES_SHADEKIN) \ No newline at end of file + sensor_mode = 3 + var/sensorpref = 5 + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/uniform.dmi', + SPECIES_VOX = 'icons/mob/species/vox/uniform.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/uniform.dmi') + +//TFF 5/8/19 - define numbers and specifics for suit sensor settings +/obj/item/clothing/under/New(var/mob/living/carbon/human/H) + ..() + sensorpref = isnull(H) ? 1 : (ishuman(H) ? H.sensorpref : 1) + switch(sensorpref) + if(1) sensor_mode = 0 //Sensors off + if(2) sensor_mode = 1 //Sensors on binary + if(3) sensor_mode = 2 //Sensors display vitals + if(4) sensor_mode = 3 //Sensors display vitals and enables tracking + if(5) sensor_mode = pick(0,1,2,3) //Select a random setting + else + sensor_mode = pick(0,1,2,3) + log_debug("Invalid switch for suit sensors, defaulting to random. [sensorpref] chosen") + +/obj/item/clothing/head + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/head.dmi', + SPECIES_VOX = 'icons/mob/species/vox/head.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/head.dmi') diff --git a/code/modules/clothing/ears/earrings.dm b/code/modules/clothing/ears/earrings.dm new file mode 100644 index 0000000000..154f69f44a --- /dev/null +++ b/code/modules/clothing/ears/earrings.dm @@ -0,0 +1,78 @@ +//Material earrings +/obj/item/clothing/ears/earring + name = "earring" + desc = "An earring of some kind." + icon = 'icons/obj/clothing/ears.dmi' + +/obj/item/clothing/ears/earring/stud + name = "pearl stud earrings" + desc = "A pair of small stud earrings." + icon_state = "ear_stud" + color = "#eae0c8" + +/obj/item/clothing/ears/earring/stud/glass + name = "glass stud earrings" + color = "#00e1ff" + +/obj/item/clothing/ears/earring/stud/wood + name = "wood stud earrings" + color = "#824b28" + +/obj/item/clothing/ears/earring/stud/iron + name = "iron stud earrings" + color = "#5c5454" + +/obj/item/clothing/ears/earring/stud/steel + name = "steel stud earrings" + color = "#666666" + +/obj/item/clothing/ears/earring/stud/silver + name = "silver stud earrings" + color = "#d1e6e3" + +/obj/item/clothing/ears/earring/stud/gold + name = "gold stud earrings" + color = "#edd12f" + +/obj/item/clothing/ears/earring/stud/platinum + name = "platinum stud earrings" + color = "#9999ff" + +/obj/item/clothing/ears/earring/stud/diamond + name = "diamond stud earrings" + color = "#00ffe1" + +/obj/item/clothing/ears/earring/dangle + icon_state = "ear_dangle" + +/obj/item/clothing/ears/earring/dangle/glass + name = "glass dangle earrings" + color = "#00e1ff" + +/obj/item/clothing/ears/earring/dangle/wood + name = "wood dangle earrings" + color = "#824b28" + +/obj/item/clothing/ears/earring/dangle/iron + name = "iron dangle earrings" + color = "#5c5454" + +/obj/item/clothing/ears/earring/dangle/steel + name = "steel dangle earrings" + color = "#666666" + +/obj/item/clothing/ears/earring/dangle/silver + name = "silver dangle earrings" + color = "#d1e6e3" + +/obj/item/clothing/ears/earring/dangle/gold + name = "gold dangle earrings" + color = "#edd12f" + +/obj/item/clothing/ears/earring/dangle/platinum + name = "platinum dangle earrings" + color = "#9999ff" + +/obj/item/clothing/ears/earring/dangle/diamond + name = "diamond dangle earrings" + color = "#00ffe1" \ No newline at end of file diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 820c5afdd4..e0167527f0 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -1,477 +1,494 @@ -/////////////////////////////////////////////////////////////////////// -//Glasses -/* -SEE_SELF // can see self, no matter what -SEE_MOBS // can see all mobs, no matter what -SEE_OBJS // can see all objs, no matter what -SEE_TURFS // can see all turfs (and areas), no matter what -SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are - // in a lit area (via pixel_x,y or smooth movement), can see those pixels -BLIND // can't see anything -*/ -/////////////////////////////////////////////////////////////////////// - -/obj/item/clothing/glasses - name = "glasses" - icon = 'icons/obj/clothing/glasses.dmi' - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_EYES - plane_slots = list(slot_glasses) - var/vision_flags = 0 - var/darkness_view = 0//Base human is 2 - var/see_invisible = -1 - var/prescription = 0 - var/toggleable = 0 - var/off_state = "degoggles" - var/active = 1 - var/activation_sound = 'sound/items/goggles_charge.ogg' - var/obj/screen/overlay = null - var/list/away_planes //Holder for disabled planes - - sprite_sheets = list( - "Teshari" = 'icons/mob/species/seromi/eyes.dmi', - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/update_clothing_icon() - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_inv_glasses() - -/obj/item/clothing/glasses/attack_self(mob/user) - if(toggleable) - if(active) - active = 0 - icon_state = off_state - user.update_inv_glasses() - flash_protection = FLASH_PROTECTION_NONE - tint = TINT_NONE - away_planes = enables_planes - enables_planes = null - to_chat(usr, "You deactivate the optical matrix on the [src].") - else - active = 1 - icon_state = initial(icon_state) - user.update_inv_glasses() - flash_protection = initial(flash_protection) - tint = initial(tint) - enables_planes = away_planes - away_planes = null - to_chat(usr, "You activate the optical matrix on the [src].") - user.update_action_buttons() - user.recalculate_vis() - ..() - -/obj/item/clothing/glasses/meson - name = "optical meson scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") - action_button_name = "Toggle Goggles" - origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) - toggleable = 1 - vision_flags = SEE_TURFS - enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) - -/obj/item/clothing/glasses/meson/New() - ..() - overlay = global_hud.meson - -/obj/item/clothing/glasses/meson/prescription - name = "prescription mesons" - desc = "Optical Meson Scanner with prescription lenses." - prescription = 1 - -/obj/item/clothing/glasses/meson/aviator - name = "engineering aviators" - icon_state = "aviator_eng" - off_state = "aviator" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - action_button_name = "Toggle HUD" - activation_sound = 'sound/effects/pop.ogg' - -/obj/item/clothing/glasses/meson/aviator/prescription - name = "prescription engineering aviators" - desc = "Engineering Aviators with prescription lenses." - prescription = 1 - -/obj/item/clothing/glasses/hud/health/aviator - name = "medical HUD aviators" - desc = "Modified aviator glasses with a toggled health HUD." - icon_state = "aviator_med" - off_state = "aviator" - action_button_name = "Toggle Mode" - toggleable = 1 - activation_sound = 'sound/effects/pop.ogg' - -/obj/item/clothing/glasses/hud/health/aviator/prescription - name = "prescription medical HUD aviators" - desc = "Modified aviator glasses with a toggled health HUD. Comes with bonus prescription lenses." - prescription = 6 - -/obj/item/clothing/glasses/science - name = "Science Goggles" - desc = "The goggles do nothing!" - icon_state = "purple" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - toggleable = 1 - action_button_name = "Toggle Goggles" - item_flags = AIRTIGHT - -/obj/item/clothing/glasses/science/New() - ..() - overlay = global_hud.science - -/obj/item/clothing/glasses/goggles - name = "goggles" - desc = "Just some plain old goggles." - icon_state = "plaingoggles" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - item_flags = AIRTIGHT - body_parts_covered = EYES - -/obj/item/clothing/glasses/night - name = "night vision goggles" - desc = "You can totally see in the dark now!" - icon_state = "night" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 2) - darkness_view = 7 - toggleable = 1 - action_button_name = "Toggle Goggles" - off_state = "denight" - flash_protection = FLASH_PROTECTION_REDUCED - enables_planes = list(VIS_FULLBRIGHT) - -/obj/item/clothing/glasses/night/vox - name = "Alien Optics" - species_restricted = list("Vox") - flags = PHORONGUARD - -/obj/item/clothing/glasses/night/New() - ..() - overlay = global_hud.nvg - -/obj/item/clothing/glasses/eyepatch - name = "eyepatch" - desc = "Yarr." - icon_state = "eyepatch" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - body_parts_covered = 0 - var/eye = null - -/obj/item/clothing/glasses/eyepatch/verb/switcheye() - set name = "Switch Eyepatch" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - - eye = !eye - if(eye) - icon_state = "[icon_state]_1" - else - icon_state = initial(icon_state) - update_clothing_icon() - -/obj/item/clothing/glasses/monocle - name = "monocle" - desc = "Such a dapper eyepiece!" - icon_state = "monocle" - item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") - body_parts_covered = 0 - -/obj/item/clothing/glasses/material - name = "optical material scanner" - desc = "Very confusing glasses." - icon_state = "material" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3) - toggleable = 1 - action_button_name = "Toggle Goggles" - vision_flags = SEE_OBJS - enables_planes = list(VIS_FULLBRIGHT) - -/obj/item/clothing/glasses/material/New() - ..() - overlay = global_hud.material - -/obj/item/clothing/glasses/material/prescription - name = "prescription optical material scanner" - prescription = 1 - -/obj/item/clothing/glasses/regular - name = "prescription glasses" - desc = "Made by Nerd. Co." - icon_state = "glasses" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - prescription = 1 - body_parts_covered = 0 - -/obj/item/clothing/glasses/regular/scanners - name = "scanning goggles" - desc = "A very oddly shaped pair of goggles with bits of wire poking out the sides. A soft humming sound emanates from it." - icon_state = "uzenwa_sissra_1" - -/obj/item/clothing/glasses/regular/hipster - name = "prescription glasses" - desc = "Made by Uncool. Co." - icon_state = "hipster_glasses" - -/obj/item/clothing/glasses/threedglasses - desc = "A long time ago, people used these glasses to makes images from screens threedimensional." - name = "3D glasses" - icon_state = "3d" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - body_parts_covered = 0 - -/obj/item/clothing/glasses/gglasses - name = "green glasses" - desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." - icon_state = "gglasses" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - body_parts_covered = 0 - -/obj/item/clothing/glasses/regular/rimless - name = "prescription rimless glasses" - desc = "Sleek modern glasses with a single sculpted lens." - icon_state = "glasses_rimless" - prescription = 1 - -/obj/item/clothing/glasses/rimless - name = "rimless glasses" - desc = "Sleek modern glasses with a single sculpted lens." - icon_state = "glasses_rimless" - prescription = 0 - -/obj/item/clothing/glasses/regular/thin - name = "prescription thin-rimmed glasses" - desc = "Glasses with frames are so last century." - icon_state = "glasses_thin" - prescription = 1 - -/obj/item/clothing/glasses/thin - name = "thin-rimmed glasses" - desc = "Glasses with frames are so last century." - icon_state = "glasses_thin" - prescription = 0 - - -/obj/item/clothing/glasses/sunglasses - name = "sunglasses" - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - icon_state = "sun" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - darkness_view = -1 - flash_protection = FLASH_PROTECTION_MODERATE - -/obj/item/clothing/glasses/sunglasses/aviator - name = "aviators" - desc = "A pair of designer sunglasses." - icon_state = "aviator" - -/obj/item/clothing/glasses/welding - name = "welding goggles" - desc = "Protects the eyes from welders, approved by the mad scientist association." - icon_state = "welding-g" - item_state_slots = list(slot_r_hand_str = "welding-g", slot_l_hand_str = "welding-g") - action_button_name = "Flip Welding Goggles" - matter = list(DEFAULT_WALL_MATERIAL = 1500, "glass" = 1000) - item_flags = AIRTIGHT - var/up = 0 - flash_protection = FLASH_PROTECTION_MAJOR - tint = TINT_HEAVY - -/obj/item/clothing/glasses/welding/attack_self() - toggle() - -/obj/item/clothing/glasses/welding/verb/toggle() - set category = "Object" - set name = "Adjust welding goggles" - set src in usr - - if(usr.canmove && !usr.stat && !usr.restrained()) - if(src.up) - src.up = !src.up - flags_inv |= HIDEEYES - body_parts_covered |= EYES - icon_state = initial(icon_state) - flash_protection = initial(flash_protection) - tint = initial(tint) - to_chat(usr, "You flip \the [src] down to protect your eyes.") - else - src.up = !src.up - flags_inv &= ~HIDEEYES - body_parts_covered &= ~EYES - icon_state = "[initial(icon_state)]up" - flash_protection = FLASH_PROTECTION_NONE - tint = TINT_NONE - to_chat(usr, "You push \the [src] up out of your face.") - update_clothing_icon() - usr.update_action_buttons() - -/obj/item/clothing/glasses/welding/superior - name = "superior welding goggles" - desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." - icon_state = "rwelding-g" - tint = TINT_MODERATE - -/obj/item/clothing/glasses/sunglasses/blindfold - name = "blindfold" - desc = "Covers the eyes, preventing sight." - icon_state = "blindfold" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - flash_protection = FLASH_PROTECTION_MAJOR - tint = BLIND - -/obj/item/clothing/glasses/sunglasses/blindfold/tape - name = "length of tape" - desc = "It's a robust DIY blindfold!" - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "tape_cross" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - w_class = ITEMSIZE_TINY - -/obj/item/clothing/glasses/sunglasses/prescription - name = "prescription sunglasses" - prescription = 1 - -/obj/item/clothing/glasses/sunglasses/big - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." - icon_state = "bigsunglasses" - -/obj/item/clothing/glasses/fakesunglasses //Sunglasses without flash immunity - name = "stylish sunglasses" - desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." - icon_state = "sun" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - -/obj/item/clothing/glasses/fakesunglasses/aviator - name = "stylish aviators" - desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." - icon_state = "aviator" - -/obj/item/clothing/glasses/sunglasses/sechud - name = "\improper HUD sunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunSecHud" - enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) - -/obj/item/clothing/glasses/sunglasses/sechud/tactical - name = "tactical HUD" - desc = "Flash-resistant goggles with inbuilt combat and security information." - icon_state = "swatgoggles" - -/obj/item/clothing/glasses/sunglasses/sechud/aviator - name = "security HUD aviators" - desc = "Modified aviator glasses that can be switch between HUD and flash protection modes." - icon_state = "aviator_sec" - off_state = "aviator" - action_button_name = "Toggle Mode" - var/on = 1 - toggleable = 1 - activation_sound = 'sound/effects/pop.ogg' - -/obj/item/clothing/glasses/sunglasses/sechud/aviator/attack_self(mob/user) - if(toggleable && !user.incapacitated()) - on = !on - if(on) - flash_protection = FLASH_PROTECTION_NONE - enables_planes = away_planes - away_planes = null - to_chat(usr, "You switch the [src] to HUD mode.") - else - flash_protection = initial(flash_protection) - away_planes = enables_planes - enables_planes = null - to_chat(usr, "You switch \the [src] to flash protection mode.") - update_icon() - user << activation_sound - user.recalculate_vis() - user.update_inv_glasses() - user.update_action_buttons() - -/obj/item/clothing/glasses/sunglasses/sechud/aviator/update_icon() - if(on) - icon_state = initial(icon_state) - else - icon_state = off_state - -/obj/item/clothing/glasses/sunglasses/sechud/aviator/prescription - name = "prescription security HUD aviators" - desc = "Modified aviator glasses that can be switch between HUD and flash protection modes. Comes with bonus prescription lenses." - prescription = 6 - -/obj/item/clothing/glasses/sunglasses/medhud - name = "\improper HUD sunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunMedHud" - enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) - -/obj/item/clothing/glasses/thermal - name = "optical thermal scanner" - desc = "Thermals in the shape of glasses." - icon_state = "thermal" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 3) - toggleable = 1 - action_button_name = "Toggle Goggles" - vision_flags = SEE_MOBS - enables_planes = list(VIS_FULLBRIGHT) - flash_protection = FLASH_PROTECTION_REDUCED - - emp_act(severity) - if(istype(src.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = src.loc - M << "The Optical Thermal Scanner overloads and blinds you!" - if(M.glasses == src) - M.Blind(3) - M.eye_blurry = 5 - // Don't cure being nearsighted - if(!(M.disabilities & NEARSIGHTED)) - M.disabilities |= NEARSIGHTED - spawn(100) - M.disabilities &= ~NEARSIGHTED - ..() - -/obj/item/clothing/glasses/thermal/New() - ..() - overlay = global_hud.thermal - -/obj/item/clothing/glasses/thermal/syndi //These are now a traitor item, concealed as mesons. -Pete - name = "optical meson scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - -/obj/item/clothing/glasses/thermal/plain - toggleable = 0 - activation_sound = null - action_button_name = null - -/obj/item/clothing/glasses/thermal/plain/monocle - name = "thermonocle" - desc = "A monocle thermal." - icon_state = "thermoncle" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - toggleable = 1 - action_button_name = "Toggle Monocle" - flags = null //doesn't protect eyes because it's a monocle, duh - - body_parts_covered = 0 - -/obj/item/clothing/glasses/thermal/plain/eyepatch - name = "optical thermal eyepatch" - desc = "An eyepatch with built-in thermal optics" - icon_state = "eyepatch" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - body_parts_covered = 0 - toggleable = 1 - action_button_name = "Toggle Eyepatch" - -/obj/item/clothing/glasses/thermal/plain/jensen - name = "optical thermal implants" - desc = "A set of implantable lenses designed to augment your vision" - icon_state = "thermalimplants" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") +/////////////////////////////////////////////////////////////////////// +//Glasses +/* +SEE_SELF // can see self, no matter what +SEE_MOBS // can see all mobs, no matter what +SEE_OBJS // can see all objs, no matter what +SEE_TURFS // can see all turfs (and areas), no matter what +SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are + // in a lit area (via pixel_x,y or smooth movement), can see those pixels +BLIND // can't see anything +*/ +/////////////////////////////////////////////////////////////////////// + +/obj/item/clothing/glasses + name = "glasses" + icon = 'icons/obj/clothing/glasses.dmi' + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_EYES + plane_slots = list(slot_glasses) + var/vision_flags = 0 + var/darkness_view = 0//Base human is 2 + var/see_invisible = -1 + var/prescription = 0 + var/toggleable = 0 + var/off_state = "degoggles" + var/active = 1 + var/activation_sound = 'sound/items/goggles_charge.ogg' + var/obj/screen/overlay = null + var/list/away_planes //Holder for disabled planes + + sprite_sheets = list( + "Teshari" = 'icons/mob/species/seromi/eyes.dmi', + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/update_clothing_icon() + if (ismob(src.loc)) + var/mob/M = src.loc + M.update_inv_glasses() + +/obj/item/clothing/glasses/attack_self(mob/user) + if(toggleable) + if(active) + active = 0 + icon_state = off_state + user.update_inv_glasses() + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + away_planes = enables_planes + enables_planes = null + to_chat(usr, "You deactivate the optical matrix on the [src].") + else + active = 1 + icon_state = initial(icon_state) + user.update_inv_glasses() + flash_protection = initial(flash_protection) + tint = initial(tint) + enables_planes = away_planes + away_planes = null + to_chat(usr, "You activate the optical matrix on the [src].") + user.update_action_buttons() + user.recalculate_vis() + ..() + +/obj/item/clothing/glasses/meson + name = "optical meson scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") + action_button_name = "Toggle Goggles" + origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) + toggleable = 1 + vision_flags = SEE_TURFS + enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) + +/obj/item/clothing/glasses/meson/New() + ..() + overlay = global_hud.meson + +/obj/item/clothing/glasses/meson/prescription + name = "prescription mesons" + desc = "Optical Meson Scanner with prescription lenses." + prescription = 1 + +/obj/item/clothing/glasses/meson/aviator + name = "engineering aviators" + icon_state = "aviator_eng" + off_state = "aviator" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + action_button_name = "Toggle HUD" + activation_sound = 'sound/effects/pop.ogg' + +/obj/item/clothing/glasses/meson/aviator/prescription + name = "prescription engineering aviators" + desc = "Engineering Aviators with prescription lenses." + prescription = 1 + +/obj/item/clothing/glasses/hud/health/aviator + name = "medical HUD aviators" + desc = "Modified aviator glasses with a toggled health HUD." + icon_state = "aviator_med" + off_state = "aviator" + action_button_name = "Toggle Mode" + toggleable = 1 + activation_sound = 'sound/effects/pop.ogg' + +/obj/item/clothing/glasses/hud/health/aviator/prescription + name = "prescription medical HUD aviators" + desc = "Modified aviator glasses with a toggled health HUD. Comes with bonus prescription lenses." + prescription = 6 + +/obj/item/clothing/glasses/science + name = "Science Goggles" + desc = "The goggles do nothing!" + icon_state = "purple" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + toggleable = 1 + action_button_name = "Toggle Goggles" + item_flags = AIRTIGHT + +/obj/item/clothing/glasses/science/New() + ..() + overlay = global_hud.science + +/obj/item/clothing/glasses/goggles + name = "goggles" + desc = "Just some plain old goggles." + icon_state = "plaingoggles" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + item_flags = AIRTIGHT + body_parts_covered = EYES + +/obj/item/clothing/glasses/night + name = "night vision goggles" + desc = "You can totally see in the dark now!" + icon_state = "night" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 2) + darkness_view = 7 + toggleable = 1 + action_button_name = "Toggle Goggles" + off_state = "denight" + flash_protection = FLASH_PROTECTION_REDUCED + enables_planes = list(VIS_FULLBRIGHT) + +/obj/item/clothing/glasses/night/vox + name = "Alien Optics" + species_restricted = list("Vox") + flags = PHORONGUARD + +/obj/item/clothing/glasses/night/New() + ..() + overlay = global_hud.nvg + +/obj/item/clothing/glasses/eyepatch + name = "eyepatch" + desc = "Yarr." + icon_state = "eyepatch" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + body_parts_covered = 0 + var/eye = null + +/obj/item/clothing/glasses/eyepatch/verb/switcheye() + set name = "Switch Eyepatch" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + eye = !eye + if(eye) + icon_state = "[icon_state]_1" + else + icon_state = initial(icon_state) + update_clothing_icon() + +/obj/item/clothing/glasses/monocle + name = "monocle" + desc = "Such a dapper eyepiece!" + icon_state = "monocle" + item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") + body_parts_covered = 0 + +/obj/item/clothing/glasses/material + name = "optical material scanner" + desc = "Very confusing glasses." + icon_state = "material" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3) + toggleable = 1 + action_button_name = "Toggle Goggles" + vision_flags = SEE_OBJS + enables_planes = list(VIS_FULLBRIGHT) + +/obj/item/clothing/glasses/material/New() + ..() + overlay = global_hud.material + +/obj/item/clothing/glasses/material/prescription + name = "prescription optical material scanner" + prescription = 1 + +/obj/item/clothing/glasses/graviton + name = "graviton goggles" + desc = "The secrets of space travel are.. not quite yours." + icon_state = "grav" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 2, TECH_BLUESPACE = 1) + darkness_view = 5 + toggleable = 1 + action_button_name = "Toggle Goggles" + off_state = "denight" + vision_flags = SEE_OBJS | SEE_TURFS + flash_protection = FLASH_PROTECTION_REDUCED + enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) + +/obj/item/clothing/glasses/graviton/New() + ..() + overlay = global_hud.material + +/obj/item/clothing/glasses/regular + name = "prescription glasses" + desc = "Made by Nerd. Co." + icon_state = "glasses" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + prescription = 1 + body_parts_covered = 0 + +/obj/item/clothing/glasses/regular/scanners + name = "scanning goggles" + desc = "A very oddly shaped pair of goggles with bits of wire poking out the sides. A soft humming sound emanates from it." + icon_state = "uzenwa_sissra_1" + +/obj/item/clothing/glasses/regular/hipster + name = "prescription glasses" + desc = "Made by Uncool. Co." + icon_state = "hipster_glasses" + +/obj/item/clothing/glasses/threedglasses + desc = "A long time ago, people used these glasses to makes images from screens threedimensional." + name = "3D glasses" + icon_state = "3d" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + body_parts_covered = 0 + +/obj/item/clothing/glasses/gglasses + name = "green glasses" + desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." + icon_state = "gglasses" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + body_parts_covered = 0 + +/obj/item/clothing/glasses/regular/rimless + name = "prescription rimless glasses" + desc = "Sleek modern glasses with a single sculpted lens." + icon_state = "glasses_rimless" + +/obj/item/clothing/glasses/rimless + name = "rimless glasses" + desc = "Sleek modern glasses with a single sculpted lens." + icon_state = "glasses_rimless" + prescription = 0 + +/obj/item/clothing/glasses/regular/thin + name = "prescription thin-rimmed glasses" + desc = "Glasses with frames are so last century." + icon_state = "glasses_thin" + prescription = 1 + +/obj/item/clothing/glasses/thin + name = "thin-rimmed glasses" + desc = "Glasses with frames are so last century." + icon_state = "glasses_thin" + prescription = 0 + + +/obj/item/clothing/glasses/sunglasses + name = "sunglasses" + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + icon_state = "sun" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + darkness_view = -1 + flash_protection = FLASH_PROTECTION_MODERATE + +/obj/item/clothing/glasses/sunglasses/aviator + name = "aviators" + desc = "A pair of designer sunglasses." + icon_state = "aviator" + +/obj/item/clothing/glasses/welding + name = "welding goggles" + desc = "Protects the eyes from welders, approved by the mad scientist association." + icon_state = "welding-g" + item_state_slots = list(slot_r_hand_str = "welding-g", slot_l_hand_str = "welding-g") + action_button_name = "Flip Welding Goggles" + matter = list(DEFAULT_WALL_MATERIAL = 1500, "glass" = 1000) + item_flags = AIRTIGHT + var/up = 0 + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY + +/obj/item/clothing/glasses/welding/attack_self() + toggle() + +/obj/item/clothing/glasses/welding/verb/toggle() + set category = "Object" + set name = "Adjust welding goggles" + set src in usr + + if(usr.canmove && !usr.stat && !usr.restrained()) + if(src.up) + src.up = !src.up + flags_inv |= HIDEEYES + body_parts_covered |= EYES + icon_state = initial(icon_state) + flash_protection = initial(flash_protection) + tint = initial(tint) + to_chat(usr, "You flip \the [src] down to protect your eyes.") + else + src.up = !src.up + flags_inv &= ~HIDEEYES + body_parts_covered &= ~EYES + icon_state = "[initial(icon_state)]up" + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You push \the [src] up out of your face.") + update_clothing_icon() + usr.update_action_buttons() + +/obj/item/clothing/glasses/welding/superior + name = "superior welding goggles" + desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." + icon_state = "rwelding-g" + tint = TINT_MODERATE + +/obj/item/clothing/glasses/sunglasses/blindfold + name = "blindfold" + desc = "Covers the eyes, preventing sight." + icon_state = "blindfold" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + flash_protection = FLASH_PROTECTION_MAJOR + tint = BLIND + +/obj/item/clothing/glasses/sunglasses/blindfold/tape + name = "length of tape" + desc = "It's a robust DIY blindfold!" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "tape_cross" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + w_class = ITEMSIZE_TINY + +/obj/item/clothing/glasses/sunglasses/prescription + name = "prescription sunglasses" + prescription = 1 + +/obj/item/clothing/glasses/sunglasses/big + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." + icon_state = "bigsunglasses" + +/obj/item/clothing/glasses/fakesunglasses //Sunglasses without flash immunity + name = "stylish sunglasses" + desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." + icon_state = "sun" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + +/obj/item/clothing/glasses/fakesunglasses/aviator + name = "stylish aviators" + desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." + icon_state = "aviator" + +/obj/item/clothing/glasses/sunglasses/sechud + name = "\improper HUD sunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunSecHud" + enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) + +/obj/item/clothing/glasses/sunglasses/sechud/tactical + name = "tactical HUD" + desc = "Flash-resistant goggles with inbuilt combat and security information." + icon_state = "swatgoggles" + +/obj/item/clothing/glasses/sunglasses/sechud/aviator + name = "security HUD aviators" + desc = "Modified aviator glasses that can be switch between HUD and flash protection modes." + icon_state = "aviator_sec" + off_state = "aviator" + action_button_name = "Toggle Mode" + var/on = 1 + toggleable = 1 + activation_sound = 'sound/effects/pop.ogg' + +/obj/item/clothing/glasses/sunglasses/sechud/aviator/attack_self(mob/user) + if(toggleable && !user.incapacitated()) + on = !on + if(on) + flash_protection = FLASH_PROTECTION_NONE + enables_planes = away_planes + away_planes = null + to_chat(usr, "You switch the [src] to HUD mode.") + else + flash_protection = initial(flash_protection) + away_planes = enables_planes + enables_planes = null + to_chat(usr, "You switch \the [src] to flash protection mode.") + update_icon() + user << activation_sound + user.recalculate_vis() + user.update_inv_glasses() + user.update_action_buttons() + +/obj/item/clothing/glasses/sunglasses/sechud/aviator/update_icon() + if(on) + icon_state = initial(icon_state) + else + icon_state = off_state + +/obj/item/clothing/glasses/sunglasses/sechud/aviator/prescription + name = "prescription security HUD aviators" + desc = "Modified aviator glasses that can be switch between HUD and flash protection modes. Comes with bonus prescription lenses." + prescription = 6 + +/obj/item/clothing/glasses/sunglasses/medhud + name = "\improper HUD sunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunMedHud" + enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) + +/obj/item/clothing/glasses/thermal + name = "optical thermal scanner" + desc = "Thermals in the shape of glasses." + icon_state = "thermal" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 3) + toggleable = 1 + action_button_name = "Toggle Goggles" + vision_flags = SEE_MOBS + enables_planes = list(VIS_FULLBRIGHT) + flash_protection = FLASH_PROTECTION_REDUCED + + emp_act(severity) + if(istype(src.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = src.loc + M << "The Optical Thermal Scanner overloads and blinds you!" + if(M.glasses == src) + M.Blind(3) + M.eye_blurry = 5 + // Don't cure being nearsighted + if(!(M.disabilities & NEARSIGHTED)) + M.disabilities |= NEARSIGHTED + spawn(100) + M.disabilities &= ~NEARSIGHTED + ..() + +/obj/item/clothing/glasses/thermal/New() + ..() + overlay = global_hud.thermal + +/obj/item/clothing/glasses/thermal/syndi //These are now a traitor item, concealed as mesons. -Pete + name = "optical meson scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") + origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) + +/obj/item/clothing/glasses/thermal/plain + toggleable = 0 + activation_sound = null + action_button_name = null + +/obj/item/clothing/glasses/thermal/plain/monocle + name = "thermonocle" + desc = "A monocle thermal." + icon_state = "thermoncle" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + toggleable = 1 + action_button_name = "Toggle Monocle" + flags = null //doesn't protect eyes because it's a monocle, duh + + body_parts_covered = 0 + +/obj/item/clothing/glasses/thermal/plain/eyepatch + name = "optical thermal eyepatch" + desc = "An eyepatch with built-in thermal optics" + icon_state = "eyepatch" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + body_parts_covered = 0 + toggleable = 1 + action_button_name = "Toggle Eyepatch" + +/obj/item/clothing/glasses/thermal/plain/jensen + name = "optical thermal implants" + desc = "A set of implantable lenses designed to augment your vision" + icon_state = "thermalimplants" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") diff --git a/code/modules/clothing/glasses/glasses_vr.dm b/code/modules/clothing/glasses/glasses_vr.dm index 1d7cb0087f..90e53d89b2 100644 --- a/code/modules/clothing/glasses/glasses_vr.dm +++ b/code/modules/clothing/glasses/glasses_vr.dm @@ -111,3 +111,10 @@ item_state = "tajblind_meson" off_state = "tajblind" body_parts_covered = EYES + +/obj/item/clothing/glasses + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/eyes.dmi', + SPECIES_VOX = 'icons/mob/species/vox/eyes.dmi', + SPECIES_WEREBEAST = 'icons/mob/species/werebeast/eyes.dmi' + ) \ No newline at end of file diff --git a/code/modules/clothing/gloves/arm_guards.dm b/code/modules/clothing/gloves/arm_guards.dm index d258cb5b63..21e7a99b35 100644 --- a/code/modules/clothing/gloves/arm_guards.dm +++ b/code/modules/clothing/gloves/arm_guards.dm @@ -44,4 +44,14 @@ icon_state = "arm_guards_combat" item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") siemens_coefficient = 0.6 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) \ No newline at end of file + armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) + +/obj/item/clothing/gloves/arm_guard/flexitac + name = "tactical arm guards" + desc = "These arm guards will protect your hands and arms from a variety of weapons while still allowing mobility." + icon_state = "arm_guards_flexitac" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + siemens_coefficient = 0.6 + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + min_cold_protection_temperature = T0C - 20 + cold_protection = ARMS \ No newline at end of file diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index a456e85939..a030bc038c 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -15,7 +15,7 @@ /obj/item/clothing/gloves/fyellow/Initialize() . = ..() //Picks a value between 0 and 1.25, in 5% increments // VOREStation edit - var/shock_pick = rand(0,25) // VOREStation Edit + var/shock_pick = rand(0,15) // VOREStation Edit siemens_coefficient = shock_pick * 0.05 /obj/item/clothing/gloves/black diff --git a/code/modules/clothing/gloves/gauntlets.dm b/code/modules/clothing/gloves/gauntlets.dm index b65884d3f9..8807fb93ad 100644 --- a/code/modules/clothing/gloves/gauntlets.dm +++ b/code/modules/clothing/gloves/gauntlets.dm @@ -18,6 +18,10 @@ var/mob/living/carbon/human/H = user if(H.gloves) gloves = H.gloves + if(!istype(gloves)) + to_chat(user, "You are unable to wear \the [src] as \the [H.gloves] are in the way.") + gloves = null + return 0 if(gloves.overgloves) to_chat(user, "You are unable to wear \the [src] as \the [H.gloves] are in the way.") gloves = null diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index bfb53f5e03..94e2d1fef0 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -127,3 +127,35 @@ overgloves = 1 force = 5 punch_force = 5 + +/obj/item/clothing/gloves/ranger + var/glovecolor = "white" + name = "ranger gloves" + desc = "The gloves of the Rangers are the least memorable part. They're not even insulated in the show, so children \ + don't try and take apart a toaster with inadequate protection. They only serve to complete the fancy outfit." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_gloves" + +/obj/item/clothing/gloves/ranger/Initialize() + . = ..() + if(icon_state == "ranger_gloves") + name = "[glovecolor] ranger gloves" + icon_state = "[glovecolor]_ranger_gloves" + +/obj/item/clothing/gloves/ranger/black + glovecolor = "black" + +/obj/item/clothing/gloves/ranger/pink + glovecolor = "pink" + +/obj/item/clothing/gloves/ranger/green + glovecolor = "green" + +/obj/item/clothing/gloves/ranger/cyan + glovecolor = "cyan" + +/obj/item/clothing/gloves/ranger/orange + glovecolor = "orange" + +/obj/item/clothing/gloves/ranger/yellow + glovecolor = "yellow" \ No newline at end of file diff --git a/code/modules/clothing/head/flowercrowns.dm b/code/modules/clothing/head/flowercrowns.dm index 6c6469de73..7e13993bcb 100644 --- a/code/modules/clothing/head/flowercrowns.dm +++ b/code/modules/clothing/head/flowercrowns.dm @@ -7,33 +7,27 @@ /obj/item/clothing/head/woodcirclet/attackby(obj/item/W as obj, mob/user as mob) var/obj/item/complete - if(istype(W,/obj/item/seeds/poppyseed)) - user << "You attach the poppy to the circlet and create a beautiful flower crown." - complete = new /obj/item/clothing/head/poppy_crown(get_turf(user)) - user.drop_from_inventory(W) - user.drop_from_inventory(src) - qdel(W) - qdel(src) - user.put_in_hands(complete) - return - else if(istype(W,/obj/item/seeds/sunflowerseed)) - user << "You attach the sunflower to the circlet and create a beautiful flower crown." - complete = new /obj/item/clothing/head/sunflower_crown(get_turf(user)) - user.drop_from_inventory(W) - user.drop_from_inventory(src) - qdel(W) - qdel(src) - user.put_in_hands(complete) - return - else if(istype(W,/obj/item/seeds/lavenderseed)) - user << "You attach the lavender to the circlet and create a beautiful flower crown." - complete = new /obj/item/clothing/head/lavender_crown(get_turf(user)) + if(istype(W, /obj/item/weapon/reagent_containers/food/snacks/grown)) + var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W + if(G.seed.kitchen_tag == "poppy") + to_chat(user, "You attach the poppy to the circlet and create a beautiful flower crown.") + complete = new /obj/item/clothing/head/poppy_crown(get_turf(user)) + else if(G.seed.kitchen_tag == "sunflower") + to_chat(user, "You attach the sunflower to the circlet and create a beautiful flower crown.") + complete = new /obj/item/clothing/head/sunflower_crown(get_turf(user)) + else if(G.seed.kitchen_tag == "lavender") + to_chat(user, "You attach the lavender to the circlet and create a beautiful flower crown.") + complete = new /obj/item/clothing/head/lavender_crown(get_turf(user)) + else if(G.seed.kitchen_tag == "rose") + to_chat(user, "You attach the rose to the circlet and create a beautiful flower crown.") + complete = new /obj/item/clothing/head/rose_crown(get_turf(user)) user.drop_from_inventory(W) user.drop_from_inventory(src) qdel(W) qdel(src) user.put_in_hands(complete) return + return ..() //Flower crowns @@ -53,4 +47,10 @@ name = "poppy crown" desc = "A flower crown weaved with poppies." icon_state = "poppy_crown" - body_parts_covered = 0 \ No newline at end of file + body_parts_covered = 0 + +/obj/item/clothing/head/rose_crown + name = "rose crown" + desc = "A flower crown weaved with roses." + icon_state = "poppy_crown" + body_parts_covered = 0 diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 558f30fbd9..e7d7cee13d 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -35,3 +35,37 @@ /obj/item/clothing/head/hardhat/dblue name = "blue hard hat" icon_state = "hardhat0_dblue" + +/obj/item/clothing/head/hardhat/ranger + var/hatcolor = "white" + name = "ranger helmet" + desc = "A special helmet designed for the Go Go ERT-Rangers, able to withstand a pressureless environment, filter gas and provide air. It has thermal vision and sometimes \ + mesons to find breaches, as well as an integrated radio... well, only in the show, of course. This one has none of those features- it just has a flashlight instead." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_helmet" + light_overlay = "helmet_light" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + +/obj/item/clothing/head/hardhat/ranger/Initialize() + . = ..() + if(icon_state == "ranger_helmet") + name = "[hatcolor] ranger helmet" + icon_state = "[hatcolor]_ranger_helmet" + +/obj/item/clothing/head/hardhat/ranger/black + hatcolor = "black" + +/obj/item/clothing/head/hardhat/ranger/pink + hatcolor = "pink" + +/obj/item/clothing/head/hardhat/ranger/green + hatcolor = "green" + +/obj/item/clothing/head/hardhat/ranger/cyan + hatcolor = "cyan" + +/obj/item/clothing/head/hardhat/ranger/orange + hatcolor = "orange" + +/obj/item/clothing/head/hardhat/ranger/yellow + hatcolor = "yellow" \ No newline at end of file diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 70c19a1260..dd290af26e 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -103,6 +103,18 @@ siemens_coefficient = 0.6 valid_accessory_slots = null +/obj/item/clothing/head/helmet/flexitac + name = "tactical light helmet" + desc = "A tan helmet made from advanced ceramic with an integrated tactical flashlight." + icon_state = "flexitac" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + brightness_on = 6 + light_overlay = "helmet_light_dual_green" + action_button_name = "Toggle Head-light" + min_cold_protection_temperature = T0C - 20 + cold_protection = HEAD + /obj/item/clothing/head/helmet/swat name = "\improper SWAT helmet" desc = "They're often used by highly trained SWAT Officers." diff --git a/code/modules/clothing/head/helmet_vr.dm b/code/modules/clothing/head/helmet_vr.dm index 8f7ec139ca..edb2d9482a 100644 --- a/code/modules/clothing/head/helmet_vr.dm +++ b/code/modules/clothing/head/helmet_vr.dm @@ -26,4 +26,4 @@ desc = "Vendi, vidi, visa; I came, I saw, I realised this hat was too expensive." icon_state = "ge_helmcent" icon = 'icons/obj/clothing/hats_vr.dmi' - icon_override = 'icons/mob/head_vr.dmi' \ No newline at end of file + icon_override = 'icons/mob/head_vr.dmi' diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index b706e71e9d..a9232aa65c 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -219,7 +219,7 @@ /obj/item/clothing/head/psy_crown name = "broken crown" desc = "A crown-of-thorns with a missing gem." - var/tension_threshold = 150 + var/tension_threshold = 125 var/cooldown = null // world.time of when this was last triggered. var/cooldown_duration = 3 MINUTES // How long the cooldown should be. var/flavor_equip = null // Message displayed to someone who puts this on their head. Drones don't get a message. @@ -273,3 +273,17 @@ /obj/item/clothing/head/psy_crown/wrath/activate_ability(var/mob/living/wearer) ..() wearer.add_modifier(/datum/modifier/berserk, 30 SECONDS) + +/obj/item/clothing/head/psy_crown/gluttony + name = "green crown" + desc = "A crown-of-thorns set with a green gemstone that seems to glow unnaturally. It feels rather disturbing to touch." + description_info = "This has a chance to cause the wearer to become extremely durable, but hungry when in extreme danger." + icon_state = "gluttonycrown" + flavor_equip = "You feel a bit hungrier after putting on this crown." + flavor_unequip = "You feel sated after removing the crown." + flavor_drop = "You feel much more sated after letting go of the crown." + flavor_activate = "An otherworldly feeling seems to enter your mind, and it drives your mind into gluttony!" + +/obj/item/clothing/head/psy_crown/gluttony/activate_ability(var/mob/living/wearer) + ..() + wearer.add_modifier(/datum/modifier/gluttonyregeneration, 45 SECONDS) diff --git a/code/modules/clothing/head/misc_vr.dm b/code/modules/clothing/head/misc_vr.dm new file mode 100644 index 0000000000..82cae35465 --- /dev/null +++ b/code/modules/clothing/head/misc_vr.dm @@ -0,0 +1,7 @@ +/obj/item/clothing/head/fish + name = "fish skull" + desc = "You... you're not actually going to wear that, right?" + icon_state = "fishskull" + icon = 'icons/obj/clothing/hats_vr.dmi' + icon_override = 'icons/mob/head_vr.dmi' + flags_inv = HIDEEARS|BLOCKHAIR diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 9319b1daa5..fceca9600b 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -1,164 +1,163 @@ -/obj/item/clothing/mask/gas - name = "gas mask" - desc = "A face-covering mask that can be connected to an air supply. Filters harmful gases from the air." - //icon = 'icons/obj/clothing/masks_vr.dmi' // Vorestation Edit? - icon_state = "gas_alt" - item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - body_parts_covered = FACE|EYES - w_class = ITEMSIZE_NORMAL - item_state_slots = list(slot_r_hand_str = "gas_alt", slot_l_hand_str = "gas_alt") - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - siemens_coefficient = 0.9 - var/gas_filter_strength = 1 //For gas mask filters - var/list/filtered_gases = list("phoron", "sleeping_agent") - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) - -/obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) - var/datum/gas_mixture/gas_filtered = new - - for(var/g in filtered_gases) - if(air.gas[g]) - gas_filtered.gas[g] = air.gas[g] * gas_filter_strength - air.gas[g] -= gas_filtered.gas[g] - - air.update_values() - gas_filtered.update_values() - - return gas_filtered - -/obj/item/clothing/mask/gas/clear - name = "gas mask" - desc = "A face-covering mask with a transparent faceplate that can be connected to an air supply." - icon_state = "gas_clear" - flags_inv = null - -/obj/item/clothing/mask/gas/half - name = "face mask" - desc = "A compact, durable gas mask that can be connected to an air supply." - icon_state = "halfgas" - siemens_coefficient = 0.7 - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) - -//Plague Dr suit can be found in clothing/suits/bio.dm -/obj/item/clothing/mask/gas/plaguedoctor - name = "plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") - armor = list(melee = 0, bullet = 0, laser = 2,energy = 2, bomb = 0, bio = 90, rad = 0) - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/gas/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - siemens_coefficient = 0.7 - body_parts_covered = FACE|EYES - -// Vox mask, has special code for eating -/obj/item/clothing/mask/gas/swat/vox - name = "\improper alien mask" - desc = "Clearly not designed for a human face." - flags = PHORONGUARD - item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - species_restricted = list(SPECIES_VOX) - filtered_gases = list("oxygen", "sleeping_agent") - var/mask_open = FALSE // Controls if the Vox can eat through this mask - action_button_name = "Toggle Feeding Port" - -/obj/item/clothing/mask/gas/swat/vox/proc/feeding_port(mob/user) - if(user.canmove && !user.stat) - mask_open = !mask_open - if(mask_open) - body_parts_covered = EYES - to_chat(user, "Your mask moves to allow you to eat.") - else - body_parts_covered = FACE|EYES - to_chat(user, "Your mask moves to cover your mouth.") - return - -/obj/item/clothing/mask/gas/swat/vox/attack_self(mob/user) - feeding_port(user) - ..() - -/obj/item/clothing/mask/gas/zaddat - name = "Zaddat Veil" - desc = "A clear survival mask used by the Zaddat to filter out harmful nitrogen. Can be connected to an air supply and reconfigured to allow for safe eating." - icon_state = "zaddat_mask" - item_state = "vax_mask" - //body_parts_covered = 0 - species_restricted = list(SPECIES_ZADDAT) - flags_inv = HIDEEARS //semi-transparent - filtered_gases = list("phoron", "nitrogen", "sleeping_agent") - -/obj/item/clothing/mask/gas/syndicate - name = "tactical mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - siemens_coefficient = 0.7 - -/obj/item/clothing/mask/gas/explorer - name = "explorer gas mask" - desc = "A military-grade gas mask that can be connected to an air supply." - icon_state = "explorer" - item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") - armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 0, bio = 50, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/mask/gas/clown_hat - name = "clown wig and mask" - desc = "A true prankster's facial attire. A clown is incomplete without their wig and mask." - icon_state = "clown" - item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") - -/obj/item/clothing/mask/gas/sexyclown - name = "sexy-clown wig and mask" - desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." - icon_state = "sexyclown" - item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") - -/obj/item/clothing/mask/gas/mime - name = "mime mask" - desc = "The traditional mime's mask. It has an eerie facial posture." - icon_state = "mime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/monkeymask - name = "monkey mask" - desc = "A mask used when acting as a monkey." - icon_state = "monkeymask" - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/gas/sexymime - name = "sexy mime mask" - desc = "A traditional female mime's mask." - icon_state = "sexymime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/guy - name = "guy fawkes mask" - desc = "A mask stylised to depict Guy Fawkes." - icon_state = "guyfawkes" - flags_inv = HIDEEARS|HIDEFACE - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/commando - name = "commando mask" - icon_state = "fullgas" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - siemens_coefficient = 0.2 - -/obj/item/clothing/mask/gas/cyborg - name = "cyborg visor" - desc = "Beep boop" - icon_state = "death" - -/obj/item/clothing/mask/gas/owl_mask - name = "owl mask" - desc = "Twoooo!" - icon_state = "owl" - body_parts_covered = HEAD|FACE|EYES +/obj/item/clothing/mask/gas + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply. Filters harmful gases from the air." + icon_state = "gas_alt" + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | ALLOW_SURVIVALFOOD + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + body_parts_covered = FACE|EYES + w_class = ITEMSIZE_NORMAL + item_state_slots = list(slot_r_hand_str = "gas_alt", slot_l_hand_str = "gas_alt") + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + siemens_coefficient = 0.9 + var/gas_filter_strength = 1 //For gas mask filters + var/list/filtered_gases = list("phoron", "sleeping_agent") + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) + +/obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) + var/datum/gas_mixture/gas_filtered = new + + for(var/g in filtered_gases) + if(air.gas[g]) + gas_filtered.gas[g] = air.gas[g] * gas_filter_strength + air.gas[g] -= gas_filtered.gas[g] + + air.update_values() + gas_filtered.update_values() + + return gas_filtered + +/obj/item/clothing/mask/gas/clear + name = "gas mask" + desc = "A face-covering mask with a transparent faceplate that can be connected to an air supply." + icon_state = "gas_clear" + flags_inv = null + +/obj/item/clothing/mask/gas/half + name = "face mask" + desc = "A compact, durable gas mask that can be connected to an air supply." + icon_state = "halfgas" + siemens_coefficient = 0.7 + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) + +//Plague Dr suit can be found in clothing/suits/bio.dm +/obj/item/clothing/mask/gas/plaguedoctor + name = "plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") + armor = list(melee = 0, bullet = 0, laser = 2,energy = 2, bomb = 0, bio = 90, rad = 0) + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/gas/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + siemens_coefficient = 0.7 + body_parts_covered = FACE|EYES + +// Vox mask, has special code for eating +/obj/item/clothing/mask/gas/swat/vox + name = "\improper alien mask" + desc = "Clearly not designed for a human face." + flags = PHORONGUARD + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + species_restricted = list(SPECIES_VOX) + filtered_gases = list("oxygen", "sleeping_agent") + var/mask_open = FALSE // Controls if the Vox can eat through this mask + action_button_name = "Toggle Feeding Port" + +/obj/item/clothing/mask/gas/swat/vox/proc/feeding_port(mob/user) + if(user.canmove && !user.stat) + mask_open = !mask_open + if(mask_open) + body_parts_covered = EYES + to_chat(user, "Your mask moves to allow you to eat.") + else + body_parts_covered = FACE|EYES + to_chat(user, "Your mask moves to cover your mouth.") + return + +/obj/item/clothing/mask/gas/swat/vox/attack_self(mob/user) + feeding_port(user) + ..() + +/obj/item/clothing/mask/gas/zaddat + name = "Zaddat Veil" + desc = "A clear survival mask used by the Zaddat to filter out harmful nitrogen. Can be connected to an air supply and reconfigured to allow for safe eating." + icon_state = "zaddat_mask" + item_state = "vax_mask" + //body_parts_covered = 0 + species_restricted = list(SPECIES_ZADDAT) + flags_inv = HIDEEARS //semi-transparent + filtered_gases = list("phoron", "nitrogen", "sleeping_agent") + +/obj/item/clothing/mask/gas/syndicate + name = "tactical mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + siemens_coefficient = 0.7 + +/obj/item/clothing/mask/gas/explorer + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + icon_state = "explorer" + item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") + armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 0, bio = 50, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/mask/gas/clown_hat + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without their wig and mask." + icon_state = "clown" + item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") + +/obj/item/clothing/mask/gas/sexyclown + name = "sexy-clown wig and mask" + desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." + icon_state = "sexyclown" + item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") + +/obj/item/clothing/mask/gas/mime + name = "mime mask" + desc = "The traditional mime's mask. It has an eerie facial posture." + icon_state = "mime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/monkeymask + name = "monkey mask" + desc = "A mask used when acting as a monkey." + icon_state = "monkeymask" + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/gas/sexymime + name = "sexy mime mask" + desc = "A traditional female mime's mask." + icon_state = "sexymime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/guy + name = "guy fawkes mask" + desc = "A mask stylised to depict Guy Fawkes." + icon_state = "guyfawkes" + flags_inv = HIDEEARS|HIDEFACE + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/commando + name = "commando mask" + icon_state = "fullgas" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + siemens_coefficient = 0.2 + +/obj/item/clothing/mask/gas/cyborg + name = "cyborg visor" + desc = "Beep boop" + icon_state = "death" + +/obj/item/clothing/mask/gas/owl_mask + name = "owl mask" + desc = "Twoooo!" + icon_state = "owl" + body_parts_covered = HEAD|FACE|EYES diff --git a/code/modules/clothing/shoes/leg_guards.dm b/code/modules/clothing/shoes/leg_guards.dm index da38116537..fba1c2cd3f 100644 --- a/code/modules/clothing/shoes/leg_guards.dm +++ b/code/modules/clothing/shoes/leg_guards.dm @@ -6,6 +6,7 @@ species_restricted = null //Unathi and Taj can wear leg armor now w_class = ITEMSIZE_NORMAL step_volume_mod = 1.3 + can_hold_knife = TRUE /obj/item/clothing/shoes/leg_guard/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = 0) if(..()) //This will only run if no other problems occured when equiping. @@ -46,3 +47,14 @@ item_state_slots = list(slot_r_hand_str = "jackboots", slot_l_hand_str = "jackboots") siemens_coefficient = 0.6 armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) + +/obj/item/clothing/shoes/leg_guard/flexitac + name = "tactical leg guards" + desc = "These will protect your legs and feet from a variety of weapons while still allowing mobility." + icon_state = "leg_guards_flexitac" + item_state_slots = list(slot_r_hand_str = "jackboots", slot_l_hand_str = "jackboots") + siemens_coefficient = 0.6 + slowdown = SHOES_SLOWDOWN+0.2 + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + min_cold_protection_temperature = T0C - 20 + cold_protection = LEGS \ No newline at end of file diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 17a44db61d..5020307699 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -2,6 +2,7 @@ desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. They're large enough to be worn over other footwear." name = "magboots" icon_state = "magboots0" + item_flags = PHORONGUARD item_state_slots = list(slot_r_hand_str = "magboots", slot_l_hand_str = "magboots") species_restricted = null force = 3 diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 0311a0fec2..e2a93f1dff 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -155,3 +155,35 @@ force = 0 w_class = ITEMSIZE_SMALL species_restricted = null + +/obj/item/clothing/shoes/boots/ranger + var/bootcolor = "white" + name = "ranger boots" + desc = "The Rangers special lightweight hybrid magboots-jetboots perfect for EVA. If only these functions were so easy to copy in reality.\ + These ones are just a well-made pair of boots in appropriate colours." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_boots" + +/obj/item/clothing/shoes/boots/ranger/Initialize() + . = ..() + if(icon_state == "ranger_boots") + name = "[bootcolor] ranger boots" + icon_state = "[bootcolor]_ranger_boots" + +/obj/item/clothing/shoes/boots/ranger/black + bootcolor = "black" + +/obj/item/clothing/shoes/boots/ranger/pink + bootcolor = "pink" + +/obj/item/clothing/shoes/boots/ranger/green + bootcolor = "green" + +/obj/item/clothing/shoes/boots/ranger/cyan + bootcolor = "cyan" + +/obj/item/clothing/shoes/boots/ranger/orange + bootcolor = "orange" + +/obj/item/clothing/shoes/boots/ranger/yellow + bootcolor = "yellow" \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index 1d449261e7..28009030a3 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -17,7 +17,7 @@ gas_transfer_coefficient = 0.01 permeability_coefficient = 0.02 item_flags = 0 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) slowdown = 1.5 armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50) diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 3e8cb36a6c..890da50468 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -89,6 +89,19 @@ new_grenade.activate(H) new_grenade.throw_at(target,fire_force,fire_distance) +/obj/item/rig_module/grenade_launcher/smoke + name = "mounted smoke-bomb launcher" + desc = "A shoulder-mounted smoke-bomb dispenser." + + interface_name = "integrated smoke-bomb launcher" + interface_desc = "Discharges loaded smoke-bombs against the wearer's location." + + fire_force = 15 + + charges = list( + list("smoke bomb", "smoke bomb", /obj/item/weapon/grenade/smokebomb, 6) + ) + /obj/item/rig_module/mounted name = "mounted laser cannon" diff --git a/code/modules/clothing/spacesuits/rig/modules/combat_vr.dm b/code/modules/clothing/spacesuits/rig/modules/combat_vr.dm new file mode 100644 index 0000000000..f62c0bf4b8 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/combat_vr.dm @@ -0,0 +1,12 @@ +/obj/item/rig_module/grenade_launcher/cleaner + name = "mounted cleaner-grenade launcher" + desc = "A shoulder-mounted cleaner-grenade dispenser." + + interface_name = "integrated cleaner-grenade launcher" + interface_desc = "Discharges loaded cleaner-grenades against the wearer's location." + + fire_force = 15 + + charges = list( + list("cleaner grenade", "cleaner grenade", /obj/item/weapon/grenade/chem_grenade/cleaner, 6) + ) diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm index b5d58c8da6..704ed7a47b 100644 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -192,8 +192,6 @@ smoke = null return ..() - - /obj/item/rig_module/self_destruct/activate() return diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index a4092d47ef..f42204a444 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -15,16 +15,19 @@ /datum/rig_vision/nvg mode = "night vision" + /datum/rig_vision/nvg/New() glasses = new /obj/item/clothing/glasses/night /datum/rig_vision/thermal mode = "thermal scanner" + /datum/rig_vision/thermal/New() glasses = new /obj/item/clothing/glasses/thermal /datum/rig_vision/meson mode = "meson scanner" + /datum/rig_vision/meson/New() glasses = new /obj/item/clothing/glasses/meson @@ -35,14 +38,22 @@ /datum/rig_vision/sechud mode = "security HUD" + /datum/rig_vision/sechud/New() glasses = new /obj/item/clothing/glasses/hud/security /datum/rig_vision/medhud mode = "medical HUD" + /datum/rig_vision/medhud/New() glasses = new /obj/item/clothing/glasses/hud/health +/datum/rig_vision/material + mode = "material scanner" + +/datum/rig_vision/material/New() + glasses = new /obj/item/clothing/glasses/material + /obj/item/rig_module/vision name = "hardsuit visor" @@ -81,6 +92,7 @@ interface_desc = "An integrated multi-mode vision system." vision_modes = list(/datum/rig_vision/meson, + /datum/rig_vision/material, /datum/rig_vision/nvg, /datum/rig_vision/thermal, /datum/rig_vision/sechud, @@ -103,7 +115,7 @@ name = "hardsuit material scanner" desc = "A layered, translucent visor system for a hardsuit." - icon_state = "material" + icon_state = "material" //VOREStation Edit usable = 0 @@ -112,6 +124,20 @@ vision_modes = list(/datum/rig_vision/material) +/obj/item/rig_module/vision/mining + + name = "hardsuit mining scanners" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "optics" + + usable = 0 + + interface_name = "mining scanners" + interface_desc = "An integrated mining scanner array." + + vision_modes = list(/datum/rig_vision/material, + /datum/rig_vision/meson) + /obj/item/rig_module/vision/thermal name = "hardsuit thermal scanner" @@ -215,4 +241,4 @@ if(!vision) vision = vision_datum processed_vision += vision_datum - vision_modes = processed_vision \ No newline at end of file + vision_modes = processed_vision diff --git a/code/modules/clothing/spacesuits/rig/rig_attackby.dm b/code/modules/clothing/spacesuits/rig/rig_attackby.dm index 3d5fc13004..67080dd1e0 100644 --- a/code/modules/clothing/spacesuits/rig/rig_attackby.dm +++ b/code/modules/clothing/spacesuits/rig/rig_attackby.dm @@ -105,7 +105,7 @@ else if(W.is_wrench()) if(!air_supply) - to_chat(user, "There is not tank to remove.") + to_chat(user, "There is no tank to remove.") return if(user.r_hand && user.l_hand) @@ -137,7 +137,7 @@ if("cell") if(cell) - to_chat(user, "You detatch \the [cell] from \the [src]'s battery mount.") + to_chat(user, "You detach \the [cell] from \the [src]'s battery mount.") for(var/obj/item/rig_module/module in installed_modules) module.deactivate() if(user.r_hand && user.l_hand) @@ -165,7 +165,7 @@ return var/obj/item/rig_module/removed = possible_removals[removal_choice] - to_chat(user, "You detatch \the [removed] from \the [src].") + to_chat(user, "You detach \the [removed] from \the [src].") removed.forceMove(get_turf(src)) removed.removed() installed_modules -= removed diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces.dm b/code/modules/clothing/spacesuits/rig/rig_pieces.dm index c402b85914..6bc6865a21 100644 --- a/code/modules/clothing/spacesuits/rig/rig_pieces.dm +++ b/code/modules/clothing/spacesuits/rig/rig_pieces.dm @@ -4,7 +4,7 @@ /obj/item/clothing/head/helmet/space/rig name = "helmet" - item_flags = THICKMATERIAL + item_flags = THICKMATERIAL|ALLOW_SURVIVALFOOD flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR body_parts_covered = HEAD|FACE|EYES heat_protection = HEAD|FACE|EYES @@ -23,7 +23,7 @@ /obj/item/clothing/gloves/gauntlets/rig name = "gauntlets" - item_flags = THICKMATERIAL + item_flags = THICKMATERIAL|PHORONGUARD body_parts_covered = HANDS heat_protection = HANDS cold_protection = HANDS diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm b/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm index 6b08a2ac00..33539575a1 100644 --- a/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm +++ b/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm @@ -35,13 +35,13 @@ ) /obj/item/clothing/head/helmet/space/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE) /obj/item/clothing/gloves/gauntlets/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE) /obj/item/clothing/shoes/magboots/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE) /obj/item/clothing/suit/space/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE) diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm b/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm new file mode 100644 index 0000000000..034ea31865 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm @@ -0,0 +1,13 @@ +/obj/item/weapon/rig/ert/janitor + name = "ERT-J suit control module" + desc = "A suit worn by the janitorial division of an Emergency Response Team. Has purple highlights. Armoured and space ready." + suit_type = "ERT janitor" + icon_state = "ert_janitor_rig" + + initial_modules = list( + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/grenade_launcher/cleaner, + ) + +/obj/item/weapon/rig/ert/assetprotection + armor = list(melee = 80, bullet = 65, laser = 50, energy = 15, bomb = 80, bio = 100, rad = 60) diff --git a/code/modules/clothing/spacesuits/rig/suits/station_vr.dm b/code/modules/clothing/spacesuits/rig/suits/station_vr.dm index 3b51d13053..2049530d02 100644 --- a/code/modules/clothing/spacesuits/rig/suits/station_vr.dm +++ b/code/modules/clothing/spacesuits/rig/suits/station_vr.dm @@ -1,13 +1,3 @@ -//Drill added for hazmat suit -/obj/item/weapon/rig/hazmat/equipped - req_access = list(access_xenoarch) - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/device/anomaly_scanner, - /obj/item/rig_module/device/drill //The suit has nothing to mine with otherwise. - ) - //Access restriction and seal delay, plus pat_module and rescue_pharm for medical suit /obj/item/weapon/rig/medical/equipped req_access = list(access_medical) @@ -21,30 +11,42 @@ ) //Armor reduction for industrial suit -/obj/item/weapon/rig/industrial +/obj/item/weapon/rig/industrial/vendor + desc = "A heavy, powerful hardsuit used by construction crews and mining corporations. This is a mass production model with reduced armor." armor = list(melee = 50, bullet = 10, laser = 20, energy = 15, bomb = 30, bio = 100, rad = 50) //Area allowing backpacks to be placed on rigsuits. /obj/item/weapon/rig/vox - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/combat - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/ert allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank, /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/tool/crowbar, \ /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/device/multitool, \ /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ - /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack) + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/light/ninja - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/cell, /obj/item/weapon/storage/backpack) + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/cell, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/merc - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs, /obj/item/weapon/storage/backpack) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/ce - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/medical - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical,/obj/item/roller,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical,/obj/item/roller,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/hazmat - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/weapon/storage/excavation,/obj/item/weapon/pickaxe,/obj/item/device/healthanalyzer,/obj/item/device/measuring_tape,/obj/item/device/ano_scanner,/obj/item/device/depth_scanner,/obj/item/device/core_sampler,/obj/item/device/gps,/obj/item/device/beacon_locator,/obj/item/device/radio/beacon,/obj/item/weapon/pickaxe/hand,/obj/item/weapon/storage/bag/fossils,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/weapon/storage/excavation,/obj/item/weapon/pickaxe,/obj/item/device/healthanalyzer,/obj/item/device/measuring_tape,/obj/item/device/ano_scanner,/obj/item/device/depth_scanner,/obj/item/device/core_sampler,/obj/item/device/gps,/obj/item/device/beacon_locator,/obj/item/device/radio/beacon,/obj/item/weapon/pickaxe/hand,/obj/item/weapon/storage/bag/fossils,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/hazard - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) /obj/item/weapon/rig/industrial - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + +/obj/item/weapon/rig/military + allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/handcuffs, \ + /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/weldingtool, /obj/item/weapon/tool, /obj/item/device/multitool, \ + /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/device/suit_cooling_unit, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) +/obj/item/weapon/rig/pmc + allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank, /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/tool/crowbar, \ + /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/device/multitool, \ + /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index 9143c1ae9b..300368260a 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -7,7 +7,7 @@ icon_state = "space" desc = "A special helmet designed for work in a hazardous, low-pressure environment." flags = PHORONGUARD - item_flags = THICKMATERIAL | AIRTIGHT + item_flags = THICKMATERIAL | AIRTIGHT | ALLOW_SURVIVALFOOD permeability_coefficient = 0.01 armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 50) flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR @@ -30,23 +30,36 @@ brightness_on = 4 on = 0 -/obj/item/clothing/head/helmet/space/attack_self(mob/user) +/obj/item/clothing/head/helmet/space/verb/toggle_camera() + set name = "Toggle Helmet Camera" + set desc = "Turn your helmet's camera on or off." + set category = "Object" + set src in usr + if(usr.stat || usr.restrained() || usr.incapacitated()) + return - if(!camera && camera_networks) + if(camera_networks) + if(!camera) + camera = new /obj/machinery/camera(src) + camera.replace_networks(camera_networks) + camera.set_status(FALSE) //So the camera will activate in the following check. - camera = new /obj/machinery/camera(src) - camera.replace_networks(camera_networks) - camera.c_tag = user.name - user << "User scanned as [camera.c_tag]. Camera activated." - user.update_action_buttons() - return 1 + if(camera.status == TRUE) + camera.set_status(FALSE) + to_chat(usr, "Camera deactivated.") + else + camera.set_status(TRUE) + camera.c_tag = usr.name + to_chat(usr, "User scanned as [camera.c_tag]. Camera activated.") - ..() + else + to_chat(usr, "This helmet does not have a built-in camera.") + return /obj/item/clothing/head/helmet/space/examine() ..() if(camera_networks && get_dist(usr,src) <= 1) - usr << "This helmet has a built-in camera. It's [camera ? "" : "in"]active." + to_chat(usr, "This helmet has a built-in camera. It's [camera ? "" : "in"]active.") /obj/item/clothing/suit/space name = "Space suit" @@ -95,18 +108,18 @@ if(user.wear_suit == src) for(var/obj/item/organ/external/E in user.bad_external_organs) if(E.is_broken() && E.apply_splint(src)) - user << "You feel [src] constrict about your [E.name], supporting it." + to_chat(user, "You feel [src] constrict about your [E.name], supporting it.") supporting_limbs |= E else // Otherwise, remove the splints. for(var/obj/item/organ/external/E in supporting_limbs) if(E.splinted == src && E.remove_splint(src)) - user << "\The [src] stops supporting your [E.name]." + to_chat(user, "\The [src] stops supporting your [E.name].") supporting_limbs.Cut() /obj/item/clothing/suit/space/proc/handle_fracture(var/mob/living/carbon/human/user, var/obj/item/organ/external/E) if(!istype(user) || isnull(supporting_limbs)) return if(E.is_broken() && E.apply_splint(src)) - user << "You feel [src] constrict about your [E.name], supporting it." + to_chat(user, "You feel [src] constrict about your [E.name], supporting it.") supporting_limbs |= E diff --git a/code/modules/clothing/spacesuits/void/station.dm b/code/modules/clothing/spacesuits/void/station.dm index 2168fbb1b5..ac652e2236 100644 --- a/code/modules/clothing/spacesuits/void/station.dm +++ b/code/modules/clothing/spacesuits/void/station.dm @@ -94,6 +94,7 @@ desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." icon_state = "rig-mining" item_state_slots = list(slot_r_hand_str = "mining_voidsuit", slot_l_hand_str = "mining_voidsuit") + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/pickaxe) armor = list(melee = 50, bullet = 5, laser = 20, energy = 5, bomb = 55, bio = 100, rad = 20) //Mining Surplus Voidsuit diff --git a/code/modules/clothing/spacesuits/void/zaddat.dm b/code/modules/clothing/spacesuits/void/zaddat.dm index cbf08d8c71..103cf39c12 100644 --- a/code/modules/clothing/spacesuits/void/zaddat.dm +++ b/code/modules/clothing/spacesuits/void/zaddat.dm @@ -42,6 +42,7 @@ switch(suit_style) if("Engineer") name = "\improper Engineer's Guild Shroud" + base_name = "\improper Engineer's Guild Shroud" desc = "This rugged Shroud was created by the Xozi Engineering Guild." icon_state = "zaddat_engie" item_state = "zaddat_engie" @@ -52,6 +53,7 @@ helmet.item_state = "zaddat_engie" if("Spacer") name = "\improper Spacer's Guild Shroud" + base_name = "\improper Spacer's Guild Shroud" desc = "The blue plastic Shroud worn by members of the Zaddat Spacer's Guild." icon_state = "zaddat_spacer" item_state = "zaddat_spacer" @@ -62,6 +64,7 @@ helmet.item_state = "zaddat_spacer" if("Knight") name = "\improper Knight's Shroud" + base_name = "\improper Knight's Shroud" desc = "This distinctive steel-plated Shroud was popularized by the Noble Guild." icon_state = "zaddat_knight" item_state = "zaddat_knight" @@ -72,6 +75,7 @@ helmet.item_state = "zaddat_knight" if("Fashion") name = "\improper Avazi House Shroud" + base_name = "\improper Avazi House Shroud" desc = "The designers of the Avazi Fashion House are among the most renowned in Zaddat society, and their Shroud designs second to none." icon_state = "zaddat_fashion" item_state = "zaddat_fashion" @@ -82,6 +86,7 @@ helmet.item_state = "zaddat_fashion" if("Bishop") name = "\improper Bishop-patterned Shroud" + base_name = "\improper Bishop-patterned Shroud" desc = "The bold designers of the Dzaz Fashion House chose to make this Bishop-themed Shroud design as a commentary on the symbiotic nature of Vanax and human culture. Allegedly." icon_state = "zaddat_bishop" item_state = "zaddat_bishop" @@ -92,6 +97,7 @@ helmet.item_state = "zaddat_bishop" if("Rugged") name = "rugged Shroud" + base_name = "rugged Shroud" desc = "This Shroud was patterned after from First Contact era human voidsuits." icon_state = "zaddat_rugged" item_state = "zaddat_rugged" diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index db33ec9662..9df94753e6 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -378,6 +378,16 @@ item_state = "tacwebvest" armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) +/obj/item/clothing/suit/storage/vest/heavy/flexitac //a reskin of the above to have a matching armor set + name = "tactical light vest" + desc = "An armored vest made from advanced flexible ceramic plates. It's surprisingly mobile, if a little unfashionable." + icon_state = "flexitac" + item_state = "flexitac" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = T0C - 20 + slowdown = 0.3 + /obj/item/clothing/suit/storage/vest/detective name = "detective armor vest" desc = "A simple kevlar plate carrier in a vintage brown, it has a badge clipped to the chest that reads, 'Private investigator'." diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index 8dab882db9..0b447470e8 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -9,7 +9,7 @@ body_parts_covered = HEAD|FACE|EYES siemens_coefficient = 0.9 flags = PHORONGUARD - item_flags = THICKMATERIAL + item_flags = THICKMATERIAL | ALLOW_SURVIVALFOOD /obj/item/clothing/suit/bio_suit name = "bio suit" diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 0f8b999b4c..91f441fa37 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -323,7 +323,7 @@ obj/item/clothing/suit/kamishimo item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") flags_inv = HIDEHOLSTER -obj/item/clothing/suit/storage/toggle/peacoat +/obj/item/clothing/suit/storage/toggle/peacoat name = "peacoat" desc = "A well-tailored, stylish peacoat." icon_state = "peacoat" @@ -338,6 +338,14 @@ obj/item/clothing/suit/storage/toggle/peacoat blood_overlay_type = "coat" allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter) flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/cardigan + name = "cardigan" + desc = "A cozy cardigan in a classic style." + icon_state = "cardigan" + addblends = "cardigan_a" + flags_inv = HIDEHOLSTER + /* * stripper */ diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 3e01478371..57fe80cd76 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -13,7 +13,7 @@ var/overlay_state = null var/concealed_holster = 0 var/mob/living/carbon/human/wearer = null //To check if the wearer changes, so species spritesheets change properly. - + var/list/on_rolled = list() //used when jumpsuit sleevels are rolled ("rolled" entry) or it's rolled down ("down"). Set to "none" to hide in those states. sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/seromi/ties.dmi') //Teshari can into webbing, too! /obj/item/clothing/accessory/Destroy() @@ -32,30 +32,38 @@ return inv_overlay /obj/item/clothing/accessory/proc/get_mob_overlay() - if(!mob_overlay || has_suit.loc != wearer) - var/tmp_icon_state = "[overlay_state? "[overlay_state]" : "[icon_state]"]" - if(ishuman(has_suit.loc)) - wearer = has_suit.loc - else - wearer = null + if(!istype(loc,/obj/item/clothing/)) //don't need special handling if it's worn as normal item. + return ..() + var/tmp_icon_state = "[overlay_state? "[overlay_state]" : "[icon_state]"]" + if(ishuman(has_suit.loc)) + wearer = has_suit.loc + else + wearer = null - if(icon_override) - if("[tmp_icon_state]_mob" in icon_states(icon_override)) - tmp_icon_state = "[tmp_icon_state]_mob" - mob_overlay = image("icon" = icon_override, "icon_state" = "[tmp_icon_state]") - else if(wearer && sprite_sheets[wearer.species.get_bodytype(wearer)]) //Teshari can finally into webbing, too! - mob_overlay = image("icon" = sprite_sheets[wearer.species.get_bodytype(wearer)], "icon_state" = "[tmp_icon_state]") - else - mob_overlay = image("icon" = INV_ACCESSORIES_DEF_ICON, "icon_state" = "[tmp_icon_state]") - if(addblends) - var/icon/base = new/icon("icon" = mob_overlay.icon, "icon_state" = mob_overlay.icon_state) - var/addblend_icon = new/icon("icon" = mob_overlay.icon, "icon_state" = src.addblends) - if(color) - base.Blend(src.color, ICON_MULTIPLY) - base.Blend(addblend_icon, ICON_ADD) - mob_overlay = image(base) - else - mob_overlay.color = src.color + if(istype(loc,/obj/item/clothing/under)) + var/obj/item/clothing/under/C = loc + if(on_rolled["down"] && C.rolled_down > 0) + tmp_icon_state = on_rolled["down"] + else if(on_rolled["rolled"] && C.rolled_sleeves > 0) + tmp_icon_state = on_rolled["rolled"] + + if(icon_override) + if("[tmp_icon_state]_mob" in icon_states(icon_override)) + tmp_icon_state = "[tmp_icon_state]_mob" + mob_overlay = image("icon" = icon_override, "icon_state" = "[tmp_icon_state]") + else if(wearer && sprite_sheets[wearer.species.get_bodytype(wearer)]) //Teshari can finally into webbing, too! + mob_overlay = image("icon" = sprite_sheets[wearer.species.get_bodytype(wearer)], "icon_state" = "[tmp_icon_state]") + else + mob_overlay = image("icon" = INV_ACCESSORIES_DEF_ICON, "icon_state" = "[tmp_icon_state]") + if(addblends) + var/icon/base = new/icon("icon" = mob_overlay.icon, "icon_state" = mob_overlay.icon_state) + var/addblend_icon = new/icon("icon" = mob_overlay.icon, "icon_state" = src.addblends) + if(color) + base.Blend(src.color, ICON_MULTIPLY) + base.Blend(addblend_icon, ICON_ADD) + mob_overlay = image(base) + else + mob_overlay.color = src.color return mob_overlay @@ -68,7 +76,7 @@ has_suit.overlays += get_inv_overlay() if(user) - user << "You attach \the [src] to \the [has_suit]." + to_chat(user, "You attach \the [src] to \the [has_suit].") add_fingerprint(user) /obj/item/clothing/accessory/proc/on_removed(var/mob/user) @@ -79,7 +87,7 @@ if(user) usr.put_in_hands(src) add_fingerprint(user) - else + else if(get_turf(src)) //We actually exist in space forceMove(get_turf(src)) //default attackby behaviour diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index cebf3ee3b8..5dc05b44c8 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -183,6 +183,16 @@ overlay_state = "collar_holo_overlay" matter = list(DEFAULT_WALL_MATERIAL = 50) +//TFF 17/6/19 - public loadout addition: Indigestible Holocollar +/obj/item/clothing/accessory/collar/holo/indigestible + name = "Holo-collar" + desc = "A special variety of the holo-collar that seems to be made of a very durable fabric that fits around the neck." + icon_state = "collar_holo" + item_state = "collar_holo_overlay" + overlay_state = "collar_holo_overlay" +//Make indigestible +/obj/item/clothing/accessory/collar/holo/indigestible/digest_act(var/atom/movable/item_storage = null) + return FALSE /obj/item/clothing/accessory/collar/attack_self(mob/user as mob) if(istype(src,/obj/item/clothing/accessory/collar/holo)) @@ -211,23 +221,23 @@ /obj/item/clothing/accessory/collar/attackby(obj/item/I, mob/user) if(istype(src,/obj/item/clothing/accessory/collar/holo)) return - + if(istype(I,/obj/item/weapon/tool/screwdriver)) update_collartag(user, I, "scratched out", "scratch out", "engraved") return - + if(istype(I,/obj/item/weapon/pen)) update_collartag(user, I, "crossed out", "cross out", "written") return - + to_chat(user,"You need a pen or a screwdriver to edit the tag on this collar.") - + /obj/item/clothing/accessory/collar/proc/update_collartag(mob/user, obj/item/I, var/erasemethod, var/erasing, var/writemethod) if(!(istype(user.get_active_hand(),I)) || !(istype(user.get_inactive_hand(),src)) || (user.stat)) return - + var/str = copytext(reject_bad_text(input(user,"Tag text?","Set tag","")),1,MAX_NAME_LEN) - + if(!str || !length(str)) if(!writtenon) to_chat(user,"You don't write anything.") diff --git a/code/modules/clothing/under/accessories/armband.dm b/code/modules/clothing/under/accessories/armband.dm index 93f4790492..e448a7d125 100644 --- a/code/modules/clothing/under/accessories/armband.dm +++ b/code/modules/clothing/under/accessories/armband.dm @@ -3,6 +3,7 @@ desc = "A fancy red armband!" icon_state = "red" slot = ACCESSORY_SLOT_ARMBAND + on_rolled = list("down" = "none") /obj/item/clothing/accessory/armband/cargo name = "cargo armband" diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 8e114c6984..55de2c36b7 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -23,6 +23,8 @@ stored_name = new_name name = "[initial(name)] ([stored_name])" +/obj/item/clothing/accessory/badge/proc/set_desc(var/mob/living/carbon/human/H) + /obj/item/clothing/accessory/badge/attack_self(mob/user as mob) if(!stored_name) diff --git a/code/modules/clothing/under/accessories/clothing.dm b/code/modules/clothing/under/accessories/clothing.dm index 66636aac6f..d5991e4f10 100644 --- a/code/modules/clothing/under/accessories/clothing.dm +++ b/code/modules/clothing/under/accessories/clothing.dm @@ -349,6 +349,25 @@ desc = "A really cheesy holiday sweater, it actually kinda itches." icon_state = "turtleneck_winterred" +/obj/item/clothing/accessory/sweater/uglyxmas + name = "ugly Christmas sweater" + desc = "A gift that probably should've stayed in the back of the closet." + icon_state = "uglyxmas" + +/obj/item/clothing/accessory/sweater/flowersweater + name = "flowery sweater" + desc = "An oversized and flowery pink sweater." + icon_state = "flowersweater" + +/obj/item/clothing/accessory/sweater/redneck + name = "red turtleneck" + desc = "A comfortable turtleneck in a dark red." + icon_state = "turtleneck_red" + +//*** +// End of sweaters +//*** + /obj/item/clothing/accessory/cowledvest name = "cowled vest" desc = "A body warmer for the 26th century." diff --git a/code/modules/clothing/under/accessories/storage.dm b/code/modules/clothing/under/accessories/storage.dm index bdd97a1148..0c1cfee9ca 100644 --- a/code/modules/clothing/under/accessories/storage.dm +++ b/code/modules/clothing/under/accessories/storage.dm @@ -8,12 +8,16 @@ var/slots = 5 var/obj/item/weapon/storage/internal/hold w_class = ITEMSIZE_NORMAL + on_rolled = list("down" = "none") + var/hide_on_roll = FALSE /obj/item/clothing/accessory/storage/New() ..() hold = new/obj/item/weapon/storage/internal(src) hold.max_storage_space = slots * 2 hold.max_w_class = ITEMSIZE_SMALL + if (!hide_on_roll) + on_rolled["down"] = icon_state /obj/item/clothing/accessory/storage/attack_hand(mob/user as mob) if (has_suit) //if we are part of a suit diff --git a/code/modules/clothing/under/accessories/torch.dm b/code/modules/clothing/under/accessories/torch.dm new file mode 100644 index 0000000000..7cdec72747 --- /dev/null +++ b/code/modules/clothing/under/accessories/torch.dm @@ -0,0 +1,713 @@ +/***** +medals +*****/ +/obj/item/clothing/accessory/medal/solgov/iron/star + name = "iron star medal" + desc = "An iron star awarded to members of the SCG for meritorious achievement or service in a combat zone." + icon_state = "iron_star" + +/obj/item/clothing/accessory/medal/solgov/iron/sol + name = "\improper Sol expeditionary medal" + desc = "An iron medal awarded to members of the SCG for service outside of the borders of the Sol Central Government." + icon_state = "iron_sol" + +/obj/item/clothing/accessory/medal/solgov/bronze/heart + name = "bronze heart medal" + desc = "A bronze heart awarded to members of the SCG for injury or death in the line of duty." + icon_state = "bronze_heart" + +/obj/item/clothing/accessory/medal/solgov/bronze/sol + name = "\improper Sol defensive operations medal" + desc = "A bronze medal awarded for members of the SCG for service defending the border regions." + icon_state = "bronze_sol" + +/obj/item/clothing/accessory/medal/solgov/silver/sword + name = "combat action medal" + desc = "A silver medal awarded to members of the SCG for honorable service while under enemy fire." + icon_state = "silver_sword" + +/obj/item/clothing/accessory/medal/solgov/silver/sol + name = "\improper Sol valor medal" + desc = "A silver medal awarded for members of the SCG for acts of exceptional valor." + icon_state = "silver_sol" + +/obj/item/clothing/accessory/medal/solgov/gold/star + name = "gold star medal" + desc = "A gold star awarded to members of the SCG for acts of heroism in a combat zone." + icon_state = "gold_star" + +/obj/item/clothing/accessory/medal/solgov/gold/sun + name = "solar service medal" + desc = "A gold medal awarded to members of the SCG by the Secretary General for significant contributions to the Sol Central Government." + icon_state = "gold_sun" + +/obj/item/clothing/accessory/medal/solgov/gold/crest + name = "solar honor medal" + desc = "A gold medal awarded to members of the Defense Forces by the Secretary General for personal acts of valor and heroism above and beyond the call of duty." + icon_state = "gold_crest" + +/obj/item/clothing/accessory/medal/solgov/gold/sol + name = "\improper Sol sapientarian medal" + desc = "A gold medal awarded for members of the SCG for significant contributions to sapient rights." + icon_state = "gold_sol" + +/obj/item/clothing/accessory/medal/solgov/heart + name = "medical medal" + desc = "A white heart emblazoned with a red cross awarded to members of the SCG for service as a medical professional in a combat zone." + icon_state = "white_heart" + +/obj/item/clothing/accessory/solgov/torch_patch + name = "\improper Torch mission patch" + desc = "A fire resistant shoulder patch, worn by the personnel involved in the Torch Project." + icon_state = "torchpatch" + on_rolled = list("down" = "none") + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/solgov/ec_patch + name = "\improper Observatory patch" + desc = "A laminated shoulder patch, carrying the symbol of the Sol Central Government Expeditionary Corpss Observatory, or SCGEO for short, the eyes and ears of the Expeditionary Corps' missions." + icon_state = "ecpatch1" + on_rolled = list("down" = "none") + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/solgov/ec_patch/fieldops + name = "\improper Field Operations patch" + desc = "A radiation-shielded shoulder patch, carrying the symbol of the Sol Central Government Expeditionary Corps Field Operations, or SCGECFO for short, the hands-on workers of every Expeditionary Corps mission." + icon_state = "ecpatch2" + +/obj/item/clothing/accessory/solgov/cultex_patch + name = "\improper Cultural Exchange patch" + desc = "A radiation-shielded shoulder patch, denoting service in the the Sol Central Government Expeditionary Corps Cultural Exchange program." + icon_state = "ecpatch3" + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/solgov/fleet_patch + name = "\improper First Fleet patch" + desc = "A fancy shoulder patch carrying insignia of First Fleet, the Sol Guard, stationed in Sol." + icon_state = "fleetpatch1" + on_rolled = list("down" = "none") + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/solgov/fleet_patch/second + name = "\improper Second Fleet patch" + desc = "A well-worn shoulder patch carrying insignia of Second Fleet, the Home Guard, tasked with defense of Sol territories." + icon_state = "fleetpatch2" + +/obj/item/clothing/accessory/solgov/fleet_patch/third + name = "\improper Third Fleet patch" + desc = "A scuffed shoulder patch carrying insignia of Third Fleet, the Border Guard, guarding borders of Sol territory against Vox and pirates." + icon_state = "fleetpatch3" + +/obj/item/clothing/accessory/solgov/fleet_patch/fourth + name = "\improper Fourth Fleet patch" + desc = "A pristine shoulder patch carrying insignia of Fourth Fleet, stationed on Skrell border." + icon_state = "fleetpatch4" + +/obj/item/clothing/accessory/solgov/fleet_patch/fifth + name = "\improper Fifth Fleet patch" + desc = "A tactical shoulder patch carrying insignia of Fifth Fleet, the Quick Reaction Force, recently formed and outfited with last tech." + icon_state = "fleetpatch5" + +/****** +ribbons +******/ +/obj/item/clothing/accessory/ribbon/solgov + name = "ribbon" + desc = "A simple military decoration." + icon_state = "ribbon_marksman" + on_rolled = list("down" = "none") + slot = ACCESSORY_SLOT_MEDAL + +/obj/item/clothing/accessory/ribbon/solgov/marksman + name = "marksmanship ribbon" + desc = "A military decoration awarded to members of the SCG for good marksmanship scores in training. Common in the days of energy weapons." + icon_state = "ribbon_marksman" + +/obj/item/clothing/accessory/ribbon/solgov/peace + name = "peacekeeping ribbon" + desc = "A military decoration awarded to members of the SCG for service during a peacekeeping operation." + icon_state = "ribbon_peace" + +/obj/item/clothing/accessory/ribbon/solgov/frontier + name = "frontier ribbon" + desc = "A military decoration awarded to members of the SCG for service along the frontier." + icon_state = "ribbon_frontier" + +/obj/item/clothing/accessory/ribbon/solgov/instructor + name = "instructor ribbon" + desc = "A military decoration awarded to members of the SCG for service as an instructor." + icon_state = "ribbon_instructor" + +/************* +specialty pins +*************/ +/obj/item/clothing/accessory/solgov/specialty + name = "speciality blaze" + desc = "A color blaze denoting fleet personnel in some special role. This one is silver." + icon_state = "marinerank_command" + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/solgov/specialty/janitor + name = "custodial blazes" + desc = "Purple blazes denoting a custodial technician." + icon_state = "fleetspec_janitor" + +/obj/item/clothing/accessory/solgov/specialty/brig + name = "brig blazes" + desc = "Red blazes denoting a brig officer." + icon_state = "fleetspec_brig" + +/obj/item/clothing/accessory/solgov/specialty/forensic + name = "forensics blazes" + desc = "Steel blazes denoting a forensic technician." + icon_state = "fleetspec_forensic" + +/obj/item/clothing/accessory/solgov/specialty/atmos + name = "atmospherics blazes" + desc = "Turquoise blazes denoting an atmospheric technician." + icon_state = "fleetspec_atmos" + +/obj/item/clothing/accessory/solgov/specialty/counselor + name = "counselor blazes" + desc = "Blue blazes denoting a counselor." + icon_state = "fleetspec_counselor" + +/obj/item/clothing/accessory/solgov/specialty/chemist + name = "chemistry blazes" + desc = "Orange blazes denoting a chemist." + icon_state = "fleetspec_chemist" + +/obj/item/clothing/accessory/solgov/specialty/enlisted + name = "enlisted qualification pin" + desc = "An iron pin denoting some special qualification." + icon_state = "fleetpin_enlisted" + +/obj/item/clothing/accessory/solgov/specialty/officer + name = "officer's qualification pin" + desc = "A golden pin denoting some special qualification." + icon_state = "fleetpin_officer" + +/obj/item/clothing/accessory/solgov/specialty/pilot + name = "pilot's qualification pin" + desc = "An iron pin denoting the qualification to fly SCG spacecraft." + icon_state = "pin_pilot" + +/***** +badges +*****/ +/obj/item/clothing/accessory/badge/solgov/security + name = "security forces badge" + desc = "A silver law enforcement badge. Stamped with the words 'Master at Arms'." + icon_state = "silverbadge" + slot_flags = SLOT_TIE + badge_string = "Sol Central Government" + +/obj/item/clothing/accessory/badge/solgov/tags + name = "dog tags" + desc = "Plain identification tags made from a durable metal. They are stamped with a variety of informational details." + gender = PLURAL + icon_state = "tags" + badge_string = "Sol Central Government" + slot_flags = SLOT_MASK | SLOT_TIE + +/obj/item/clothing/accessory/badge/solgov/tags/Initialize() + . = ..() + var/mob/living/carbon/human/H + H = get_holder_of_type(src, /mob/living/carbon/human) + if(H) + set_name(H.real_name) + set_desc(H) + +/obj/item/clothing/accessory/badge/solgov/tags/set_desc(var/mob/living/carbon/human/H) + if(!istype(H)) + return + var/religion = "Unset" + desc = "[initial(desc)]\nName: [H.real_name] ([H.get_species()])\nReligion: [religion]\nBlood type: [H.b_type]" + +/obj/item/clothing/accessory/badge/solgov/representative + name = "representative's badge" + desc = "A leather-backed plastic badge with a variety of information printed on it. Belongs to a representative of the Sol Central Government." + icon_state = "solbadge" + slot_flags = SLOT_TIE + badge_string = "Sol Central Government" + +/******* +armbands +*******/ +/obj/item/clothing/accessory/armband/solgov/mp + name = "military police brassard" + desc = "An armlet, worn by the crew to display which department they're assigned to. This one is black with 'MP' in white." + icon_state = "mpband" + +/obj/item/clothing/accessory/armband/solgov/ma + name = "master at arms brassard" + desc = "An armlet, worn by the crew to display which department they're assigned to. This one is white with 'MA' in navy blue." + icon_state = "maband" + +/***************** +armour attachments +*****************/ +/obj/item/clothing/accessory/armor/tag/solgov + name = "\improper SCG Flag" + desc = "An emblem depicting the Sol Central Government's flag." + icon_state = "solflag" + slot = ACCESSORY_SLOT_ARMOR_M + +/obj/item/clothing/accessory/armor/tag/solgov/ec + name = "\improper Expeditionary Corps crest" + desc = "An emblem depicting the crest of the SCG Expeditionary Corps." + icon_state = "ecflag" + +/obj/item/clothing/accessory/armor/tag/solgov/sec + name = "\improper POLICE tag" + desc = "An armor tag with the word POLICE printed in silver lettering on it." + icon_state = "sectag" + +/obj/item/clothing/accessory/armor/tag/solgov/medic + name = "\improper MEDIC tag" + desc = "An armor tag with the word MEDIC printed in red lettering on it." + icon_state = "medictag" + +/obj/item/clothing/accessory/armor/tag/solgov/agent + name = "\improper OCIE AGENT tag" + desc = "An armor tag with the word OCIE AGENT printed in gold lettering on it." + icon_state = "agenttag" + +/obj/item/clothing/accessory/armor/tag/solgov/com + name = "\improper SCG tag" + desc = "An armor tag with the words SOL CENTRAL GOVERNMENT printed in gold lettering on it." + icon_state = "comtag" + +/obj/item/clothing/accessory/armor/tag/solgov/com/sec + name = "\improper POLICE tag" + desc = "An armor tag with the words POLICE printed in gold lettering on it." + +/obj/item/clothing/accessory/armor/helmcover/blue/sol + name = "peacekeeper helmet cover" + desc = "A fabric cover for armored helmets. This one is in SCG peacekeeper colors." + +/************** +department tags +**************/ +/obj/item/clothing/accessory/solgov/department + name = "department insignia" + desc = "Insignia denoting assignment to a department. These appear blank." + icon_state = "dept_exped" + on_rolled = list("down" = "none", "rolled" = "dept_exped_sleeves") + slot = ACCESSORY_SLOT_DECOR + //removable = FALSE + +/obj/item/clothing/accessory/solgov/department/command + name = "command insignia" + desc = "Insignia denoting assignment to the command department. These fit Expeditionary Corps uniforms." + color = "#e5ea4f" + +/obj/item/clothing/accessory/solgov/department/command/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/command/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the command department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/command/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the command department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/engineering + name = "engineering insignia" + desc = "Insignia denoting assignment to the engineering department. These fit Expeditionary Corps uniforms." + color = "#ff7f00" + +/obj/item/clothing/accessory/solgov/department/engineering/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/engineering/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the engineering department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/engineering/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the engineering department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/security + name = "security insignia" + desc = "Insignia denoting assignment to the security department. These fit Expeditionary Corps uniforms." + color = "#bf0000" + +/obj/item/clothing/accessory/solgov/department/security/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/security/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the security department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/security/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the security department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/medical + name = "medical insignia" + desc = "Insignia denoting assignment to the medical department. These fit Expeditionary Corps uniforms." + color = "#4c9ce4" + +/obj/item/clothing/accessory/solgov/department/medical/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/medical/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the medical department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/medical/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the medical department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/supply + name = "supply insignia" + desc = "Insignia denoting assignment to the supply department. These fit Expeditionary Corps uniforms." + color = "#bb9042" + +/obj/item/clothing/accessory/solgov/department/supply/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/supply/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the supply department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/supply/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the supply department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/service + name = "service insignia" + desc = "Insignia denoting assignment to the service department. These fit Expeditionary Corps uniforms." + color = "#6eaa2c" + +/obj/item/clothing/accessory/solgov/department/service/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/service/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the service department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/service/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the service department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/exploration + name = "exploration insignia" + desc = "Insignia denoting assignment to the exploration department. These fit Expeditionary Corps uniforms." + color = "#68099e" + +/obj/item/clothing/accessory/solgov/department/exploration/service + icon_state = "dept_exped_service" + +/obj/item/clothing/accessory/solgov/department/exploration/fleet + icon_state = "dept_fleet" + desc = "Insignia denoting assignment to the exploration department. These fit Fleet uniforms." + on_rolled = list("rolled" = "dept_fleet_sleeves", "down" = "none") + +/obj/item/clothing/accessory/solgov/department/exploration/marine + icon_state = "dept_marine" + desc = "Insignia denoting assignment to the exploration department. These fit marine uniforms." + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/department/research + name = "research insignia" + desc = "Insignia denoting assignment to the research department. These fit Expeditionary Corps uniforms." + color = "#68099e" + +/obj/item/clothing/accessory/solgov/department/research/service + icon_state = "dept_exped_service" + +/********* +ranks - ec +*********/ + +/obj/item/clothing/accessory/solgov/rank + name = "ranks" + desc = "Insignia denoting rank of some kind. These appear blank." + icon_state = "fleetrank" + on_rolled = list("down" = "none") + slot = ACCESSORY_SLOT_DECOR + gender = PLURAL + //high_visibility = 1 + +/obj/item/clothing/accessory/solgov/rank/ec + name = "explorer ranks" + desc = "Insignia denoting rank of some kind. These appear blank." + icon_state = "ecrank_e1" + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/rank/ec/enlisted + name = "ranks (E-1 apprentice explorer)" + desc = "Insignia denoting the rank of Apprentice Explorer." + icon_state = "ecrank_e1" + +/obj/item/clothing/accessory/solgov/rank/ec/enlisted/e3 + name = "ranks (E-3 explorer)" + desc = "Insignia denoting the rank of Explorer." + icon_state = "ecrank_e3" + +/obj/item/clothing/accessory/solgov/rank/ec/enlisted/e5 + name = "ranks (E-5 senior explorer)" + desc = "Insignia denoting the rank of Senior Explorer." + icon_state = "ecrank_e5" + +/obj/item/clothing/accessory/solgov/rank/ec/enlisted/e7 + name = "ranks (E-7 chief explorer)" + desc = "Insignia denoting the rank of Chief Explorer." + icon_state = "ecrank_e7" + +/obj/item/clothing/accessory/solgov/rank/ec/officer + name = "ranks (O-1 ensign)" + desc = "Insignia denoting the rank of Ensign." + icon_state = "ecrank_o1" + +/obj/item/clothing/accessory/solgov/rank/ec/officer/o3 + name = "ranks (O-3 lieutenant)" + desc = "Insignia denoting the rank of Lieutenant." + icon_state = "ecrank_o3" + +/obj/item/clothing/accessory/solgov/rank/ec/officer/o5 + name = "ranks (O-5 commander)" + desc = "Insignia denoting the rank of Commander." + icon_state = "ecrank_o5" + +/obj/item/clothing/accessory/solgov/rank/ec/officer/o6 + name = "ranks (O-6 captain)" + desc = "Insignia denoting the rank of Captain." + icon_state = "ecrank_o6" + +/obj/item/clothing/accessory/solgov/rank/ec/officer/o8 + name = "ranks (O-8 admiral)" + desc = "Insignia denoting the rank of Admiral." + icon_state = "ecrank_o8" + +/************ +ranks - fleet +************/ +/obj/item/clothing/accessory/solgov/rank/fleet + name = "naval ranks" + desc = "Insignia denoting naval rank of some kind. These appear blank." + icon_state = "fleetrank" + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted + name = "ranks (E-1 crewman recruit)" + desc = "Insignia denoting the rank of Crewman Recruit." + icon_state = "fleetrank_enlisted" + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e2 + name = "ranks (E-2 crewman apprentice)" + desc = "Insignia denoting the rank of Crewman Apprentice." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e3 + name = "ranks (E-3 crewman)" + desc = "Insignia denoting the rank of Crewman." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e4 + name = "ranks (E-4 petty officer third class)" + desc = "Insignia denoting the rank of Petty Officer Third Class." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e5 + name = "ranks (E-5 petty officer second class)" + desc = "Insignia denoting the rank of Petty Officer Second Class." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e6 + name = "ranks (E-6 petty officer first class)" + desc = "Insignia denoting the rank of Petty Officer First Class." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e7 + name = "ranks (E-7 chief petty officer)" + desc = "Insignia denoting the rank of Chief Petty Officer." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e8 + name = "ranks (E-8 senior chief petty officer)" + desc = "Insignia denoting the rank of Senior Chief Petty Officer." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e9 + name = "ranks (E-9 master chief petty officer)" + desc = "Insignia denoting the rank of Master Chief Petty Officer." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e9_alt1 + name = "ranks (E-9 command master chief petty officer)" + desc = "Insignia denoting the rank of Command Master Chief Petty Officer." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e9_alt2 + name = "ranks (E-9 fleet master chief petty officer)" + desc = "Insignia denoting the rank of Fleet Master Chief Petty Officer." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e9_alt3 + name = "ranks (E-9 force master chief petty officer)" + desc = "Insignia denoting the rank of Force Master Chief Petty Officer." + +/obj/item/clothing/accessory/solgov/rank/fleet/enlisted/e9_alt4 + name = "ranks (E-9 master chief petty officer of the Fleet)" + desc = "Insignia denoting the rank of Master Chief Petty Officer of the Fleet." + +/obj/item/clothing/accessory/solgov/rank/fleet/officer + name = "ranks (O-1 ensign)" + desc = "Insignia denoting the rank of Ensign." + icon_state = "fleetrank_officer" + +/obj/item/clothing/accessory/solgov/rank/fleet/officer/wo1_monkey + name = "makeshift ranks (WO-1 warrant officer 1)" + desc = "Insignia denoting the mythical rank of Warrant Officer. Too bad it's obviously fake." + +/obj/item/clothing/accessory/solgov/rank/fleet/officer/o2 + name = "ranks (O-2 sub-lieutenant)" + desc = "Insignia denoting the rank of Sub-lieutenant." + +/obj/item/clothing/accessory/solgov/rank/fleet/officer/o3 + name = "ranks (O-3 lieutenant)" + desc = "Insignia denoting the rank of Lieutenant." + +/obj/item/clothing/accessory/solgov/rank/fleet/officer/o4 + name = "ranks (O-4 lieutenant commander)" + desc = "Insignia denoting the rank of Lieutenant Commander." + +/obj/item/clothing/accessory/solgov/rank/fleet/officer/o5 + name = "ranks (O-5 commander)" + desc = "Insignia denoting the rank of Commander." + +/obj/item/clothing/accessory/solgov/rank/fleet/officer/o6 + name = "ranks (O-6 captain)" + desc = "Insignia denoting the rank of Captain." + icon_state = "fleetrank_command" + +/obj/item/clothing/accessory/solgov/rank/fleet/flag + name = "ranks (O-7 commodore)" + desc = "Insignia denoting the rank of Commodore." + icon_state = "fleetrank_command" + +/obj/item/clothing/accessory/solgov/rank/fleet/flag/o8 + name = "ranks (O-8 rear admiral)" + desc = "Insignia denoting the rank of Rear Admiral." + +/obj/item/clothing/accessory/solgov/rank/fleet/flag/o9 + name = "ranks (O-9 vice admiral)" + desc = "Insignia denoting the rank of Vice Admiral." + +/obj/item/clothing/accessory/solgov/rank/fleet/flag/o10 + name = "ranks (O-10 admiral)" + desc = "Insignia denoting the rank of Admiral." + +/obj/item/clothing/accessory/solgov/rank/fleet/flag/o10_alt + name = "ranks (O-10 fleet admiral)" + desc = "Insignia denoting the rank of Fleet Admiral." + +/************** +ranks - marines +**************/ +/obj/item/clothing/accessory/solgov/rank/marine + name = "marine ranks" + desc = "Insignia denoting marine rank of some kind. These appear blank." + icon_state = "marinerank_enlisted" + on_rolled = list("down" = "none") + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted + name = "ranks (E-1 private)" + desc = "Insignia denoting the rank of Private." + icon_state = "marinerank_enlisted" + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e2 + name = "ranks (E-2 private second class)" + desc = "Insignia denoting the rank of Private Second Class." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e3 + name = "ranks (E-3 private first class)" + desc = "Insignia denoting the rank of Private First Class." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e4 + name = "ranks (E-4 corporal)" + desc = "Insignia denoting the rank of Corporal." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e5 + name = "ranks (E-5 sergeant)" + desc = "Insignia denoting the rank of Sergeant." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e6 + name = "ranks (E-6 staff sergeant)" + desc = "Insignia denoting the rank of Staff Sergeant." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e7 + name = "ranks (E-7 sergeant first class)" + desc = "Insignia denoting the rank of Sergeant First Class." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e8 + name = "ranks (E-8 master sergeant)" + desc = "Insignia denoting the rank of Master Sergeant." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e8_alt + name = "ranks (E-8 first sergeant)" + desc = "Insignia denoting the rank of First Sergeant." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e9 + name = "ranks (E-9 sergeant major)" + desc = "Insignia denoting the rank of Sergeant Major." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e9_alt1 + name = "ranks (E-9 command sergeant major)" + desc = "Insignia denoting the rank of Command Sergeant Major." + +/obj/item/clothing/accessory/solgov/rank/marine/enlisted/e9_alt2 + name = "ranks (E-9 sergeant major of the marine)" + desc = "Insignia denoting the rank of Sergeant Major of the marine." + +/obj/item/clothing/accessory/solgov/rank/marine/officer + name = "ranks (O-1 second lieutenant)" + desc = "Insignia denoting the rank of Second Lieutenant." + icon_state = "marinerank_officer" + +/obj/item/clothing/accessory/solgov/rank/marine/officer/o2 + name = "ranks (O-2 first lieutenant)" + desc = "Insignia denoting the rank of First Lieutenant." + +/obj/item/clothing/accessory/solgov/rank/marine/officer/o3 + name = "ranks (O-3 captain)" + desc = "Insignia denoting the rank of Captain." + +/obj/item/clothing/accessory/solgov/rank/marine/officer/o4 + name = "ranks (O-4 major)" + desc = "Insignia denoting the rank of Major." + +/obj/item/clothing/accessory/solgov/rank/marine/officer/o5 + name = "ranks (O-5 lieutenant colonel)" + desc = "Insignia denoting the rank of Lieutenant Colonel." + +/obj/item/clothing/accessory/solgov/rank/marine/officer/o6 + name = "ranks (O-6 colonel)" + desc = "Insignia denoting the rank of Colonel." + +/obj/item/clothing/accessory/solgov/rank/marine/flag + name = "ranks (O-7 brigadier general)" + desc = "Insignia denoting the rank of Brigadier General." + icon_state = "marinerank_command" + +/obj/item/clothing/accessory/solgov/rank/marine/flag/o8 + name = "ranks (O-8 major general)" + desc = "Insignia denoting the rank of Major General." + +/obj/item/clothing/accessory/solgov/rank/marine/flag/o9 + name = "ranks (O-9 lieutenant general)" + desc = "Insignia denoting the rank of lieutenant general." + +/obj/item/clothing/accessory/solgov/rank/marine/flag/o10 + name = "ranks (O-10 general)" + desc = "Insignia denoting the rank of General." + +/obj/item/clothing/accessory/solgov/rank/marine/flag/o10_alt + name = "ranks (O-10 field marshal)" + desc = "Insignia denoting the rank of Field Marshal." diff --git a/code/modules/clothing/under/accessories/torch_vr.dm b/code/modules/clothing/under/accessories/torch_vr.dm new file mode 100644 index 0000000000..71ce5fb45d --- /dev/null +++ b/code/modules/clothing/under/accessories/torch_vr.dm @@ -0,0 +1,23 @@ +/obj/item/clothing/accessory/solgov/department/command + desc = "Insignia denoting assignment to the command department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/engineering + desc = "Insignia denoting assignment to the engineering department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/security + desc = "Insignia denoting assignment to the security department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/medical + desc = "Insignia denoting assignment to the medical department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/supply + desc = "Insignia denoting assignment to the supply department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/exploration + desc = "Insignia denoting assignment to the exploration department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/research + desc = "Insignia denoting assignment to the research department. These fit Society of Universal Cartographers uniforms." + +/obj/item/clothing/accessory/solgov/department/service + desc = "Insignia denoting assignment to the service department. These fit Society of Universal Cartographers uniforms." \ No newline at end of file diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index cc5d7ed917..9638a414bd 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -30,13 +30,19 @@ icon_state = "grey" rolled_sleeves = 0 +//TFF 5/8/19 - add a non perma-set orange jumpsuit, splits prison into its own obj with override var settings. +//TFF 5/9/19 - add a different icon_state to both jumpsuits, orange and prison. Refactors orange and prison jumpsuit slightly. /obj/item/clothing/under/color/orange name = "orange jumpsuit" - desc = "It's standardized prisoner-wear. Its suit sensors are permanently set to the \"Tracking\" position." icon_state = "orange" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/prison + name = "prison jumpsuit" + desc = "It's standardized prisoner-wear. Its suit sensors are permanently set to the \"Tracking\" position." + icon_state = "prison" has_sensor = 2 sensor_mode = 3 - rolled_sleeves = 0 /obj/item/clothing/under/color/pink name = "pink jumpsuit" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 943577663f..c7ccb6a31a 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -453,6 +453,10 @@ icon_state = "sari_green" item_state_slots = list(slot_r_hand_str = "dress_green", slot_l_hand_str = "dress_green") +/obj/item/clothing/under/dress/lilacdress + name = "lilac dress" + desc = "A simple black dress adorned in fake purple lilacs." + icon_state = "lilacdress" /* * wedding stuff @@ -904,3 +908,40 @@ name = "hazard cohesion suit" desc = "An orange cohesion suit with yellow hazard stripes intended to assist Prometheans in maintaining their form and prevent direct skin exposure." icon_state = "cohesionsuit_hazard" + +//Uniforms +//On-mob sprites go in icons\mob\uniform.dmi with the format "white_ranger_uniform_s" - with 'white' replaced with green, cyan, etc... of course! Note the _s - this is not optional. +//Item sprites go in icons\obj\clothing\ranger.dmi with the format "white_ranger_uniform" +/obj/item/clothing/under/color/ranger + var/unicolor = "white" + name = "ranger uniform" + desc = "Made from a space-proof fibre and tight fitting, this uniform usually gives the agile Rangers all kinds of protection while not inhibiting their movement. \ + This costume is instead made from genuine cotton fibre and is based on the season three uniform." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_uniform" + rolled_down = 0 + rolled_sleeves = 0 + +/obj/item/clothing/under/color/ranger/Initialize() + . = ..() + if(icon_state == "ranger_uniform") //allows for custom items + name = "[unicolor] ranger uniform" + icon_state = "[unicolor]_ranger_uniform" + +/obj/item/clothing/under/color/ranger/black + unicolor = "black" + +/obj/item/clothing/under/color/ranger/pink + unicolor = "pink" + +/obj/item/clothing/under/color/ranger/green + unicolor = "green" + +/obj/item/clothing/under/color/ranger/cyan + unicolor = "cyan" + +/obj/item/clothing/under/color/ranger/orange + unicolor = "orange" + +/obj/item/clothing/under/color/ranger/yellow + unicolor = "yellow" \ No newline at end of file diff --git a/code/modules/clothing/under/miscellaneous_vr.dm b/code/modules/clothing/under/miscellaneous_vr.dm index eb95b123b4..2cdc692b0b 100644 --- a/code/modules/clothing/under/miscellaneous_vr.dm +++ b/code/modules/clothing/under/miscellaneous_vr.dm @@ -110,3 +110,7 @@ H.resize(original_size) original_size = null H.visible_message("The space around [H] distorts as they return to their original size!","The space around you distorts as you return to your original size!") + +//Same as Nanotrasen Security Uniforms +/obj/item/clothing/under/ert + armor = list(melee = 5, bullet = 10, laser = 10, energy = 5, bomb = 5, bio = 0, rad = 0) \ No newline at end of file diff --git a/code/modules/clothing/under/nanotrasen_vr.dm b/code/modules/clothing/under/nanotrasen_vr.dm index aff19ec6e4..f6eeecfb43 100644 --- a/code/modules/clothing/under/nanotrasen_vr.dm +++ b/code/modules/clothing/under/nanotrasen_vr.dm @@ -13,7 +13,7 @@ desc = "The security uniform of NanoTrasen's security. It looks sturdy and well padded" icon_state = "navyutility_sec" worn_state = "navyutility_sec" - armor = list(melee = 10, bullet = 10, laser = 10,energy = 10, bomb = 10, bio = 10, rad = 10) + armor = list(melee = 10, bullet = 5, laser = 5, energy = 5, bomb = 5, bio = 0, rad = 0) /obj/item/clothing/under/nanotrasen/security/warden name = "NanoTrasen warden uniform" @@ -37,7 +37,7 @@ slot_l_hand_str = "darkbluesoft", slot_r_hand_str = "darkbluesoft", ) - armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 5, bio = 5, rad = 0) + armor = list(melee = 10, bullet = 5, laser = 5, energy = 5, bomb = 5, bio = 0, rad = 0) /obj/item/clothing/head/beret/nanotrasen name = "NanoTrasen security beret" diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index 4ac2ad5f2e..26a00a4a63 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -130,6 +130,12 @@ desc = "A piece of cloth wrapped around the waist." icon_state = "loincloth" +/obj/item/clothing/under/skirt/pleated + name = "pleated skirt" + desc = "A simple pleated skirt. It's like high school all over again." + icon_state = "pleated" + addblends = "pleated_a" + /obj/item/clothing/under/skirt/outfit name = "black skirt" desc = "A black skirt, very fancy!" diff --git a/code/modules/clothing/under/solgov.dm b/code/modules/clothing/under/solgov.dm index 39109c480a..0859cdf1e4 100644 --- a/code/modules/clothing/under/solgov.dm +++ b/code/modules/clothing/under/solgov.dm @@ -1,7 +1,18 @@ //SolGov Uniforms +//Master +/obj/item/clothing/under/solgov + name = "master solgov uniform" + desc = "You shouldn't be seeing this." + icon = 'icons/obj/clothing/uniforms_solgov.dmi' + rolled_down = 0 + rolled_sleeves = 0 + item_icons = list(slot_w_uniform_str = 'icons/mob/uniform_solgov.dmi') + armor = list(melee = 5, bullet = 0, laser = 5, energy = 5, bomb = 0, bio = 5, rad = 5) + siemens_coefficient = 0.8 + //PT -/obj/item/clothing/under/pt +/obj/item/clothing/under/solgov/pt name = "pt uniform" desc = "Shorts! Shirt! Miami! Sexy!" icon_state = "miami" @@ -10,19 +21,19 @@ siemens_coefficient = 0.9 body_parts_covered = UPPER_TORSO|LOWER_TORSO -/obj/item/clothing/under/pt/sifguard +/obj/item/clothing/under/solgov/pt/sifguard name = "\improper SifGuard pt uniform" desc = "A baggy shirt bearing the seal of the Sif Defense Force and some dorky looking blue shorts." icon_state = "expeditionpt" worn_state = "expeditionpt" -/obj/item/clothing/under/pt/fleet +/obj/item/clothing/under/solgov/pt/fleet name = "fleet pt uniform" desc = "A pair of black shorts and two tank tops, seems impractical. Looks good though." icon_state = "fleetpt" worn_state = "fleetpt" -/obj/item/clothing/under/pt/marine +/obj/item/clothing/under/solgov/pt/marine name = "marine pt uniform" desc = "Does NOT leave much to the imagination." icon_state = "marinept" @@ -53,63 +64,62 @@ worn_state = "greyutility" //Here's the real ones -/obj/item/clothing/under/utility/sifguard +/obj/item/clothing/under/solgov/utility/sifguard name = "\improper SifGuard uniform" desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has silver trim." icon_state = "blackutility_crew" worn_state = "blackutility_crew" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 10) -/obj/item/clothing/under/utility/sifguard/medical - name = "\improper SifGuard medical uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has silver trim and blue blazes." - icon_state = "blackutility_med" - worn_state = "blackutility_med" +/obj/item/clothing/under/solgov/utility/sifguard/command + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/command) -/obj/item/clothing/under/utility/sifguard/medical/command - name = "\improper SifGuard medical command uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has gold trim and blue blazes." - icon_state = "blackutility_medcom" - worn_state = "blackutility_medcom" +/obj/item/clothing/under/solgov/utility/sifguard/engineering + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/engineering) -/obj/item/clothing/under/utility/sifguard/engineering - name = "\improper SifGuard engineering uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has silver trim and organge blazes." - icon_state = "blackutility_eng" - worn_state = "blackutility_eng" +/obj/item/clothing/under/solgov/utility/sifguard/security + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/security) -/obj/item/clothing/under/utility/sifguard/engineering/command - name = "\improper SifGuard engineering command uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has gold trim and organge blazes." - icon_state = "blackutility_engcom" - worn_state = "blackutility_engcom" +/obj/item/clothing/under/solgov/utility/sifguard/medical + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/medical) -/obj/item/clothing/under/utility/sifguard/supply - name = "\improper SifGuard supply uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has silver trim and brown blazes." - icon_state = "blackutility_sup" - worn_state = "blackutility_sup" +/obj/item/clothing/under/solgov/utility/sifguard/supply + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/supply) -/obj/item/clothing/under/utility/sifguard/security - name = "\improper SifGuard security uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has silver trim and red blazes." - icon_state = "blackutility_sec" - worn_state = "blackutility_sec" +/obj/item/clothing/under/solgov/utility/sifguard/exploration + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/exploration) -/obj/item/clothing/under/utility/sifguard/security/command - name = "\improper SifGuard security command uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has gold trim and red blazes." - icon_state = "blackutility_seccom" - worn_state = "blackutility_seccom" +/obj/item/clothing/under/solgov/utility/sifguard/research + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/research) -/obj/item/clothing/under/utility/sifguard/command - name = "\improper SifGuard command uniform" - desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has gold trim and gold blazes." +/obj/item/clothing/under/solgov/utility/sifguard/officer + name = "\improper Sifuard officer's uniform" + desc = "The utility uniform of the Sif Defense Force, made from biohazard resistant material. This one has gold trim." icon_state = "blackutility_com" worn_state = "blackutility_com" +/obj/item/clothing/under/solgov/utility/sifguard/officer/command + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/command) -/obj/item/clothing/under/utility/fleet +/obj/item/clothing/under/solgov/utility/sifguard/officer/engineering + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/engineering) + +/obj/item/clothing/under/solgov/utility/sifguard/officer/security + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/security) + +/obj/item/clothing/under/solgov/utility/sifguard/officer/medical + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/medical) + +/obj/item/clothing/under/solgov/utility/sifguard/officer/supply + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/supply) + +/obj/item/clothing/under/solgov/utility/sifguard/officer/exploration + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/exploration) + +/obj/item/clothing/under/solgov/utility/sifguard/officer/research + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/research) + +/obj/item/clothing/under/solgov/utility/fleet name = "fleet coveralls" desc = "The utility uniform of the SCG Fleet, made from an insulated material." icon_state = "navyutility" @@ -117,116 +127,96 @@ armor = list(melee = 0, bullet = 0, laser = 0,energy = 10, bomb = 0, bio = 0, rad = 0) siemens_coefficient = 0.7 -/obj/item/clothing/under/utility/fleet/medical - name = "fleet medical coveralls" - desc = "The utility uniform of the SCG Fleet, made from an insulated material. This one has blue cuffs." - icon_state = "navyutility_med" - worn_state = "navyutility_med" +/obj/item/clothing/under/solgov/utility/fleet/command + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/command/fleet) -/obj/item/clothing/under/utility/fleet/engineering - name = "fleet engineering coveralls" - desc = "The utility uniform of the SCG Fleet, made from an insulated material. This one has orange cuffs." - icon_state = "navyutility_eng" - worn_state = "navyutility_eng" +/obj/item/clothing/under/solgov/utility/fleet/command/pilot + starting_accessories = list(/obj/item/clothing/accessory/solgov/specialty/pilot) -/obj/item/clothing/under/utility/fleet/supply - name = "fleet supply coveralls" - desc = "The utility uniform of the SCG Fleet, made from an insulated material. This one has brown cuffs." - icon_state = "navyutility_sup" - worn_state = "navyutility_sup" +/obj/item/clothing/under/solgov/utility/fleet/engineering + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/engineering/fleet) -/obj/item/clothing/under/utility/fleet/security - name = "fleet security coveralls" - desc = "The utility uniform of the SCG Fleet, made from an insulated material. This one has red cuffs." - icon_state = "navyutility_sec" - worn_state = "navyutility_sec" +/obj/item/clothing/under/solgov/utility/fleet/security + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/security/fleet) -/obj/item/clothing/under/utility/fleet/command - name = "fleet command coveralls" - desc = "The utility uniform of the SCG Fleet, made from an insulated material. This one has gold cuffs." - icon_state = "navyutility_com" - worn_state = "navyutility_com" +/obj/item/clothing/under/solgov/utility/fleet/medical + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/medical/fleet) + +/obj/item/clothing/under/solgov/utility/fleet/supply + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/supply/fleet) + +/obj/item/clothing/under/solgov/utility/fleet/exploration + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/exploration/fleet) -/obj/item/clothing/under/utility/marine +/obj/item/clothing/under/solgov/utility/marine name = "marine fatigues" desc = "The utility uniform of the SCG Marine Corps, made from durable material." icon_state = "greyutility" worn_state = "greyutility" armor = list(melee = 10, bullet = 0, laser = 10,energy = 0, bomb = 0, bio = 0, rad = 0) -/obj/item/clothing/under/utility/marine/green +/obj/item/clothing/under/solgov/utility/marine/command + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/command/marine) + +/obj/item/clothing/under/solgov/utility/marine/engineering + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/engineering/marine) + +/obj/item/clothing/under/solgov/utility/marine/security + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/security/marine) + +/obj/item/clothing/under/solgov/utility/marine/medical + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/medical/marine) + +/obj/item/clothing/under/solgov/utility/marine/supply + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/supply/marine) + +/obj/item/clothing/under/solgov/utility/marine/exploration + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/exploration/marine) + +/obj/item/clothing/under/solgov/utility/marine/green name = "green fatigues" desc = "A green version of the SCG marine utility uniform, made from durable material." icon_state = "greenutility" worn_state = "greenutility" -/obj/item/clothing/under/utility/marine/tan +/obj/item/clothing/under/solgov/utility/marine/tan name = "tan fatigues" desc = "A tan version of the SCG marine utility uniform, made from durable material." icon_state = "tanutility" worn_state = "tanutility" -/obj/item/clothing/under/utility/marine/medical - name = "marine medical fatigues" - desc = "The utility uniform of the SCG Marine Corps, made from durable material. This one has blue markings." - icon_state = "greyutility_med" - worn_state = "greyutility_med" - -/obj/item/clothing/under/utility/marine/engineering - name = "marine engineering fatigues" - desc = "The utility uniform of the SCG Marine Corps, made from durable material. This one has orange markings." - icon_state = "greyutility_eng" - worn_state = "greyutility_eng" - -/obj/item/clothing/under/utility/marine/supply - name = "marine supply fatigues" - desc = "The utility uniform of the SCG Marine Corps, made from durable material. This one has brown markings." - icon_state = "greyutility_sup" - worn_state = "greyutility_sup" - -/obj/item/clothing/under/utility/marine/security - name = "marine security fatigues" - desc = "The utility uniform of the SCG Marine Corps, made from durable material. This one has red markings." - icon_state = "greyutility_sec" - worn_state = "greyutility_sec" - -/obj/item/clothing/under/utility/marine/command - name = "marine command coveralls" - desc = "The utility uniform of the SCG Marine Corps, made from durable material. This one has gold markings." - icon_state = "greyutility_com" - worn_state = "greyutility_com" - //Service -/obj/item/clothing/under/service +/obj/item/clothing/under/solgov/service name = "service uniform" desc = "A service uniform of some kind." icon_state = "whiteservice" worn_state = "whiteservice" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + armor = list(melee = 5, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 5, rad = 0) siemens_coefficient = 0.9 -/obj/item/clothing/under/service/fleet +/obj/item/clothing/under/solgov/service/fleet name = "fleet service uniform" desc = "The service uniform of the SCG Fleet, made from immaculate white fabric." icon_state = "whiteservice" worn_state = "whiteservice" -/obj/item/clothing/under/service/marine +/obj/item/clothing/under/solgov/service/marine name = "marine service uniform" desc = "The service uniform of the SCG Marine Corps. Slimming." icon_state = "greenservice" worn_state = "greenservice" -/obj/item/clothing/under/service/marine/command +/obj/item/clothing/under/solgov/service/marine/command name = "marine command service uniform" desc = "The service uniform of the SCG Marine Corps. Slimming and stylish." icon_state = "greenservice_com" worn_state = "greenservice_com" //Dress -/obj/item/clothing/under/mildress + +/obj/item/clothing/under/solgov/mildress name = "dress uniform" desc = "A dress uniform of some kind." icon_state = "greydress" @@ -234,25 +224,71 @@ armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) siemens_coefficient = 0.9 -/obj/item/clothing/under/mildress/sifguard +/obj/item/clothing/under/solgov/mildress/sifguard name = "\improper SifGuard dress uniform" desc = "The dress uniform of the Sif Defense Force in silver trim." - icon_state = "greydress" - worn_state = "greydress" -/obj/item/clothing/under/mildress/sifguard/command +/obj/item/clothing/under/solgov/mildress/sifguard/command + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/command/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/engineering + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/engineering/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/security + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/security/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/medical + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/medical/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/supply + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/supply/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/service + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/service/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/exploration + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/exploration/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/research + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/research/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer name = "\improper SifGuard command dress uniform" desc = "The dress uniform of the Sif Defense Force in gold trim." icon_state = "greydress_com" worn_state = "greydress_com" -/obj/item/clothing/under/mildress/marine +/obj/item/clothing/under/solgov/mildress/sifguard/officer/command + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/command/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/engineering + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/engineering/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/security + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/security/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/medical + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/medical/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/supply + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/supply/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/service + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/service/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/exploration + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/exploration/service) + +/obj/item/clothing/under/solgov/mildress/sifguard/officer/research + starting_accessories = list(/obj/item/clothing/accessory/solgov/department/research/service) + +/obj/item/clothing/under/solgov/mildress/marine name = "marine dress uniform" desc = "The dress uniform of the SCG Marine Corps, class given form." icon_state = "blackdress" worn_state = "blackdress" -/obj/item/clothing/under/mildress/marine/command +/obj/item/clothing/under/solgov/mildress/marine/command name = "marine command dress uniform" desc = "The dress uniform of the SCG Marine Corps, even classier in gold." icon_state = "blackdress_com" diff --git a/code/modules/clothing/under/solgov_vr.dm b/code/modules/clothing/under/solgov_vr.dm index d08d91f0cc..eb6e793685 100644 --- a/code/modules/clothing/under/solgov_vr.dm +++ b/code/modules/clothing/under/solgov_vr.dm @@ -1,15 +1,15 @@ //SolGov Uniforms //PT -/obj/item/clothing/under/pt/sifguard +/obj/item/clothing/under/solgov/pt/sifguard name = "explorer's pt uniform" desc = "A baggy shirt bearing the seal of the Society of Universal Cartographers and some dorky looking blue shorts." -/obj/item/clothing/under/pt/fleet +/obj/item/clothing/under/solgov/pt/fleet name = "fleet pt uniform" desc = "A pair of black shorts and two tank tops, seems impractical. Looks good though." -/obj/item/clothing/under/pt/marine +/obj/item/clothing/under/solgov/pt/marine name = "marine pt uniform" desc = "Does NOT leave much to the imagination." @@ -20,81 +20,32 @@ name = "utility uniform" desc = "A comfortable turtleneck and black utility trousers." -/obj/item/clothing/under/utility/sifguard +/obj/item/clothing/under/solgov/utility/sifguard name = "explorer's uniform" desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has silver trim." -/obj/item/clothing/under/utility/sifguard/medical - name = "explorer's medical uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has silver trim and blue blazes." - -/obj/item/clothing/under/utility/sifguard/medical/command - name = "explorer's medical command uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has gold trim and blue blazes." - -/obj/item/clothing/under/utility/sifguard/engineering - name = "explorer's engineering uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has silver trim and organge blazes." - -/obj/item/clothing/under/utility/sifguard/engineering/command - name = "explorer's engineering command uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has gold trim and organge blazes." - -/obj/item/clothing/under/utility/sifguard/supply - name = "explorer's supply uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has silver trim and brown blazes." - -/obj/item/clothing/under/utility/sifguard/security - name = "explorer's security uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has silver trim and red blazes." - -/obj/item/clothing/under/utility/sifguard/security/command - name = "explorer's security command uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has gold trim and red blazes." - -/obj/item/clothing/under/utility/sifguard/command - name = "explorer's command uniform" - desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has gold trim and gold blazes." +/obj/item/clothing/under/solgov/utility/sifguard/officer + name = "explorer's officer uniform" + desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This one has gold trim." -/obj/item/clothing/under/utility/fleet +/obj/item/clothing/under/solgov/utility/fleet name = "fleet coveralls" desc = "The utility uniform of the USDF Fleet, made from an insulated material." -/obj/item/clothing/under/utility/fleet/medical - name = "fleet medical coveralls" - desc = "The utility uniform of the USDF Fleet, made from an insulated material. This one has blue cuffs." - -/obj/item/clothing/under/utility/fleet/engineering - name = "fleet engineering coveralls" - desc = "The utility uniform of the USDF Fleet, made from an insulated material. This one has orange cuffs." - -/obj/item/clothing/under/utility/fleet/supply - name = "fleet supply coveralls" - desc = "The utility uniform of the USDF Fleet, made from an insulated material. This one has brown cuffs." - -/obj/item/clothing/under/utility/fleet/security - name = "fleet security coveralls" - desc = "The utility uniform of the USDF Fleet, made from an insulated material. This one has red cuffs." - -/obj/item/clothing/under/utility/fleet/command - name = "fleet command coveralls" - desc = "The utility uniform of the USDF Fleet, made from an insulated material. This one has gold cuffs." - - -/obj/item/clothing/under/utility/marine +/obj/item/clothing/under/solgov/utility/marine name = "marine fatigues" desc = "The utility uniform of the USDF Marine Corps, made from durable material." -/obj/item/clothing/under/utility/marine/green +/obj/item/clothing/under/solgov/utility/marine/green name = "green fatigues" desc = "A green version of the USDF marine utility uniform, made from durable material." -/obj/item/clothing/under/utility/marine/tan +/obj/item/clothing/under/solgov/utility/marine/tan name = "tan fatigues" desc = "A tan version of the USDF marine utility uniform, made from durable material." -/obj/item/clothing/under/utility/marine/olive +/obj/item/clothing/under/solgov/utility/marine/olive name = "olive fatigues" desc = "An olive version of the USDF marine utility uniform, made from durable material." icon = 'icons/obj/clothing/uniforms_vr.dmi' @@ -102,7 +53,7 @@ icon_state = "bdu_olive" item_state = "bdu_olive" -/obj/item/clothing/under/utility/marine/desert +/obj/item/clothing/under/solgov/utility/marine/desert name = "desert fatigues" desc = "A desert version of the USDF marine utility uniform, made from durable material." icon = 'icons/obj/clothing/uniforms_vr.dmi' @@ -110,53 +61,33 @@ icon_state = "bdu_olive" item_state = "bdu_olive" -/obj/item/clothing/under/utility/marine/medical - name = "marine medical fatigues" - desc = "The utility uniform of the USDF Marine Corps, made from durable material. This one has blue markings." - -/obj/item/clothing/under/utility/marine/engineering - name = "marine engineering fatigues" - desc = "The utility uniform of the USDF Marine Corps, made from durable material. This one has orange markings." - -/obj/item/clothing/under/utility/marine/supply - name = "marine supply fatigues" - desc = "The utility uniform of the USDF Marine Corps, made from durable material. This one has brown markings." - -/obj/item/clothing/under/utility/marine/security - name = "marine security fatigues" - desc = "The utility uniform of the USDF Marine Corps, made from durable material. This one has red markings." - -/obj/item/clothing/under/utility/marine/command - name = "marine command coveralls" - desc = "The utility uniform of the USDF Marine Corps, made from durable material. This one has gold markings." - //Service -/obj/item/clothing/under/service/fleet +/obj/item/clothing/under/solgov/service/fleet name = "fleet service uniform" desc = "The service uniform of the USDF Fleet, made from immaculate white fabric." -/obj/item/clothing/under/service/marine +/obj/item/clothing/under/solgov/service/marine name = "marine service uniform" desc = "The service uniform of the USDF Marine Corps. Slimming." worn_state = "greenservice" -/obj/item/clothing/under/service/marine/command +/obj/item/clothing/under/solgov/service/marine/command name = "marine command service uniform" desc = "The service uniform of the USDF Marine Corps. Slimming and stylish." -/obj/item/clothing/under/mildress/expeditionary +/obj/item/clothing/under/solgov/mildress/expeditionary name = "explorer's dress uniform" desc = "The dress uniform of the Society of Universal Cartographers in silver trim." -/obj/item/clothing/under/mildress/expeditionary/command +/obj/item/clothing/under/solgov/mildress/expeditionary/command name = "explorer's command dress uniform" desc = "The dress uniform of the Society of Universal Cartographers in gold trim." -/obj/item/clothing/under/mildress/marine +/obj/item/clothing/under/solgov/mildress/marine name = "marine dress uniform" desc = "The dress uniform of the USDF Marine Corps, class given form." -/obj/item/clothing/under/mildress/marine/command +/obj/item/clothing/under/solgov/mildress/marine/command name = "marine command dress uniform" desc = "The dress uniform of the USDF Marine Corps, even classier in gold." \ No newline at end of file diff --git a/code/modules/events/camera_damage.dm b/code/modules/events/camera_damage.dm index 3205a93137..6a86581b70 100644 --- a/code/modules/events/camera_damage.dm +++ b/code/modules/events/camera_damage.dm @@ -33,6 +33,6 @@ return acquire_random_camera(remaining_attempts--) /datum/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) - // Only return a functional camera, not installed in a silicon, and that exists somewhere players have access + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access var/turf/T = get_turf(C) - return T && C.can_use() && !istype(C.loc, /mob/living/silicon) && (T.z in using_map.player_levels) + return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index dee085dc2b..a4c55b27ae 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -22,6 +22,9 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT var/last_world_time = 0 /datum/event_container/process() + if(!round_start_time) + return //don't do events if the round hasn't even started yet + if(!next_event_time) set_event_delay() diff --git a/code/modules/events/event_container_vr.dm b/code/modules/events/event_container_vr.dm index 05472dc638..0212435c71 100644 --- a/code/modules/events/event_container_vr.dm +++ b/code/modules/events/event_container_vr.dm @@ -74,7 +74,7 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Solar Storm", /datum/event/solar_storm, 30, list(ASSIGNMENT_ENGINEER = 40, ASSIGNMENT_SECURITY = 30), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Virology Breach", /datum/event/prison_break/virology, 0, list(ASSIGNMENT_MEDICAL = 100), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Xenobiology Breach", /datum/event/prison_break/xenobiology, 0, list(ASSIGNMENT_SCIENCE = 100), 1), - //new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grub Infestation", /datum/event/grub_infestation, 40, list(ASSIGNMENT_SECURITY = 50, ASSIGNMENT_ENGINEER = 50), 1), //VORESTATION AI TEMPORARY REMOVAL + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grub Infestation", /datum/event/grub_infestation, 0, list(ASSIGNMENT_SECURITY = 20, ASSIGNMENT_ENGINEER = 30), 1), //Evil grubs that drain station power slightly ) add_disabled_events(list( diff --git a/code/modules/events/grubinfestation_vr.dm b/code/modules/events/grubinfestation_vr.dm index 210e21e441..bdb7b634fa 100644 --- a/code/modules/events/grubinfestation_vr.dm +++ b/code/modules/events/grubinfestation_vr.dm @@ -9,7 +9,7 @@ /datum/event/grub_infestation/setup() announceWhen = rand(announceWhen, announceWhen + 60) - spawncount = rand(4 * severity, 6 * severity) //grub larva only have a 50% chance to grow big and strong + spawncount = rand(2 * severity, 6 * severity) for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) if(istype(get_area(temp_vent), /area/crew_quarters/sleep)) @@ -24,7 +24,7 @@ /datum/event/grub_infestation/start() while((spawncount >= 1) && vents.len) var/obj/vent = pick(vents) - //new /mob/living/simple_mob/solargrub_larva(get_turf(vent)) //VORESTATION AI TEMPORARY REMOVAL. Event commented out until mobs are fixed. + new /mob/living/simple_mob/animal/solargrub_larva(get_turf(vent)) vents -= vent spawncount-- vents.Cut() diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 0bf0f82aa6..c50b44dc26 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -14,8 +14,8 @@ for (var/mob/living/silicon/ai/target in silicon_mob_list) var/law = target.generate_ion_law() - target << "You have detected a change in your laws information:" - target << law + to_chat(target, "You have detected a change in your laws information:") + to_chat(target, law) target.add_ion_law(law) target.show_laws() /* //VOREstation edit. Was fucking up all PDA messagess. diff --git a/code/modules/events/money_hacker.dm b/code/modules/events/money_hacker.dm index 47734f412d..0e604b9956 100644 --- a/code/modules/events/money_hacker.dm +++ b/code/modules/events/money_hacker.dm @@ -1,4 +1,4 @@ -/var/global/account_hack_attempted = 0 +//var/global/account_hack_attempted = 0 /datum/event/money_hacker var/datum/money_account/affected_account diff --git a/code/modules/events/money_spam.dm b/code/modules/events/money_spam.dm index 3343a03c44..ced7c9a45f 100644 --- a/code/modules/events/money_spam.dm +++ b/code/modules/events/money_spam.dm @@ -101,11 +101,13 @@ last_spam_time = world.time + /* //VOREStation Removal: no need to spam the AI tenfold if (prob(50)) //Give the AI an increased chance to intercept the message for(var/mob/living/silicon/ai/ai in mob_list) // Allows other AIs to intercept the message but the AI won't intercept their own message. if(ai.aiPDA != P && ai.aiPDA != src) ai.show_message("Intercepted message from [sender] (Unknown / spam?) to [P:owner]: [message]") + */ //Commented out because we don't send messages like this anymore. Instead it will just popup in their chat window. //P.tnote += "← From [sender] (Unknown / spam?):
      [message]
      " diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm index 6140ae2de0..13f853dd3c 100644 --- a/code/modules/events/radiation_storm.dm +++ b/code/modules/events/radiation_storm.dm @@ -31,7 +31,7 @@ /datum/event/radiation_storm/proc/radiate() var/radiation_level = rand(15, 35) for(var/z in using_map.station_levels) - radiation_repository.z_radiate(locate(1, 1, z), radiation_level, 1) + SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) for(var/mob/living/carbon/C in living_mob_list) var/area/A = get_area(C) diff --git a/code/modules/events/viral_infection.dm b/code/modules/events/viral_infection.dm index 8d0ea8b7ee..d24e774ed8 100644 --- a/code/modules/events/viral_infection.dm +++ b/code/modules/events/viral_infection.dm @@ -1,4 +1,4 @@ -/var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed +//var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed datum/event/viral_infection var/list/viruses = list() diff --git a/code/modules/events/viral_outbreak.dm b/code/modules/events/viral_outbreak.dm index 708b6c203c..453d101773 100644 --- a/code/modules/events/viral_outbreak.dm +++ b/code/modules/events/viral_outbreak.dm @@ -1,17 +1,17 @@ -datum/event/viral_outbreak +/datum/event/viral_outbreak var/severity = 1 -datum/event/viral_outbreak/setup() +/datum/event/viral_outbreak/setup() announceWhen = rand(0, 3000) endWhen = announceWhen + 1 severity = rand(2, 4) -datum/event/viral_outbreak/announce() +/datum/event/viral_outbreak/announce() command_alert("Confirmed outbreak of level 7 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert") world << sound('sound/AI/outbreak7.ogg') -datum/event/viral_outbreak/start() +/datum/event/viral_outbreak/start() var/list/candidates = list() //list of candidate keys for(var/mob/living/carbon/human/G in player_list) if(G.client && G.stat != DEAD) diff --git a/code/modules/fishing/fishing.dm b/code/modules/fishing/fishing.dm new file mode 100644 index 0000000000..7e4a9d85bc --- /dev/null +++ b/code/modules/fishing/fishing.dm @@ -0,0 +1,202 @@ +/* + * Fishing! Contains normal fishing rods and nets. + */ + +GLOBAL_LIST_INIT(generic_fishing_rare_list, list( + /mob/living/simple_mob/animal/passive/fish/solarfish = 1, + /mob/living/simple_mob/animal/passive/fish/icebass = 5, + /mob/living/simple_mob/animal/passive/fish/koi = 3 + )) + +GLOBAL_LIST_INIT(generic_fishing_uncommon_list, list( + /mob/living/simple_mob/animal/passive/fish/salmon = 6, + /mob/living/simple_mob/animal/passive/fish/pike = 10, + /mob/living/simple_mob/animal/passive/fish/javelin = 3, + /mob/living/simple_mob/animal/passive/crab/sif = 1 + )) + +GLOBAL_LIST_INIT(generic_fishing_common_list, list( + /mob/living/simple_mob/animal/passive/fish/bass = 10, + /mob/living/simple_mob/animal/passive/fish/trout = 8, + /mob/living/simple_mob/animal/passive/fish/perch = 6, + /mob/living/simple_mob/animal/passive/fish/murkin = 8, + /mob/living/simple_mob/animal/passive/fish/rockfish = 5, + /mob/living/simple_mob/animal/passive/crab = 1 + )) + +GLOBAL_LIST_INIT(generic_fishing_junk_list, list( + /obj/item/clothing/shoes/boots/cowboy = 1, + /obj/random/fishing_junk = 10 + )) + +GLOBAL_LIST_INIT(generic_fishing_pool_list, list( + /obj/item/weapon/bikehorn/rubberducky = 5, + /obj/item/toy/plushie/carp = 20, + /obj/random/junk = 80, + /obj/random/trash = 80, + /obj/item/weapon/spacecash/c1 = 10, + /obj/item/weapon/spacecash/c10 = 5, + /obj/item/weapon/spacecash/c100 = 1 + )) + +#define FISHING_RARE "rare" +#define FISHING_UNCOMMON "uncommon" +#define FISHING_COMMON "common" +#define FISHING_JUNK "junk" +#define FISHING_NOTHING "nothing" + +GLOBAL_LIST_INIT(generic_fishing_chance_list, list(FISHING_RARE = 5, FISHING_UNCOMMON = 15, FISHING_COMMON = 30, FISHING_JUNK = 30, FISHING_NOTHING = 40)) + +/turf/simulated/floor/water + var/has_fish = TRUE //If the water has fish or not. + + var/list/rare_fish_list // Rare list. + + var/list/uncommon_fish_list // Uncommon list. + + var/list/common_fish_list // Common list. + + var/list/junk_list // Junk item list. + + var/list/fishing_loot // Chance list. + + var/fishing_cooldown = 30 SECONDS + var/last_fished = 0 + + var/fish_type + var/min_fishing_time = 30 // Time in seconds. + var/max_fishing_time = 90 + + var/being_fished = FALSE + +/turf/simulated/floor/water/proc/handle_fish() // Subtypes should over-ride this, and supply their own GLOB lists for maximum Mix and Match power. + if(has_fish) + rare_fish_list = GLOB.generic_fishing_rare_list + uncommon_fish_list = GLOB.generic_fishing_uncommon_list + common_fish_list = GLOB.generic_fishing_common_list + junk_list = GLOB.generic_fishing_junk_list + fishing_loot = GLOB.generic_fishing_chance_list + +/turf/simulated/floor/water/pool + has_fish = FALSE + +/turf/simulated/floor/water/deep/pool + has_fish = TRUE + +/turf/simulated/floor/water/deep/pool/handle_fish() + if(has_fish) + rare_fish_list = GLOB.generic_fishing_pool_list + uncommon_fish_list = GLOB.generic_fishing_pool_list + common_fish_list = GLOB.generic_fishing_pool_list + junk_list = GLOB.generic_fishing_pool_list + fishing_loot = GLOB.generic_fishing_chance_list + +/turf/simulated/floor/water/ex_act(severity) // Explosive fishing. + if(prob(5 * severity)) + pick_fish() + if(fish_type) + var/fished = new fish_type(get_turf(src)) + if(isliving(fished)) + var/mob/living/L = fished + L.death() + has_fish = FALSE + ..(severity) + +/turf/simulated/floor/water/proc/pick_fish() + if(has_fish) + var/table = pickweight(fishing_loot) + if(table == FISHING_RARE && rare_fish_list.len) + fish_type = pickweight(rare_fish_list) + else if(table == FISHING_UNCOMMON && uncommon_fish_list.len) + fish_type = pickweight(uncommon_fish_list) + else if(table == FISHING_COMMON && common_fish_list.len) + fish_type = pickweight(common_fish_list) + else if(table == FISHING_JUNK && junk_list.len) + fish_type = pickweight(junk_list) + else + fish_type = null + else + fish_type = null + +/turf/simulated/floor/water/attackby(obj/item/weapon/P as obj, mob/user as mob) +//If you use a fishing rod on an open body of water that var/has_fish enabled + if(istype(P, /obj/item/weapon/material/fishing_rod) && !being_fished) + var/obj/item/weapon/material/fishing_rod/R = P + if(!R.strung) + to_chat(user, "It is hard to go fishing without any line!") + return + if(R.cast) + to_chat(user, "You can only cast one line at a time!") + return + playsound(src, 'sound/effects/slosh.ogg', 5, 1, 5) + to_chat(user,"You cast \the [P.name] into \the [src].") + being_fished = TRUE + R.cast = TRUE + var/fishing_time = rand(min_fishing_time SECONDS,max_fishing_time SECONDS) * R.toolspeed + if(do_after(user,fishing_time,user)) + playsound(src, 'sound/effects/slosh.ogg', 5, 1, 5) + to_chat(user,"You feel a tug and begin pulling!") + if(world.time >= last_fished + fishing_cooldown) + pick_fish() + last_fished = world.time + else + fish_type = null + if(prob(3)) // No fish left here.. + has_fish = FALSE + //List of possible outcomes. + if(!fish_type) + to_chat(user,"You caught... nothing. How sad.") + else + var/fished = new fish_type(get_turf(user)) + if(isliving(fished)) + R.consume_bait() + var/mob/living/L = fished + if(prob(rand(L.mob_size) + 10) && R.line_break) + R.strung = FALSE + R.update_icon() + user.visible_message("\The [R]'s string snaps!") + if(prob(33)) // Dead on hook. Good for food, not so much for live catch. + L.death() + to_chat(user,"You fish out \the [fished] from the water with [P.name]!") + R.cast = FALSE + being_fished = FALSE + else ..() + +/obj/random/fishing_junk + name = "junk" + desc = "This is a random fishing junk item." + icon = 'icons/obj/storage.dmi' + icon_state = "red" + +/obj/random/fishing_junk/item_to_spawn() + return pickweight(list( + /obj/random/toy = 60, + /obj/random/maintenance/engineering = 50, + /obj/random/maintenance/clean = 40, + /obj/random/maintenance/security = 40, + /obj/random/maintenance/research = 40, + /obj/structure/closet/crate/secure/loot = 30, + /obj/random/bomb_supply = 30, + /obj/random/powercell = 30, + /obj/random/tech_supply/component = 30, + /obj/random/unidentified_medicine/old_medicine = 30, + /obj/random/plushie = 30, + /obj/random/contraband = 20, + /obj/random/coin = 20, + /obj/random/medical = 15, + /obj/random/unidentified_medicine/fresh_medicine = 15, + /obj/random/action_figure = 15, + /obj/random/plushielarge = 15, + /obj/random/firstaid = 10, + /obj/random/tool/powermaint = 5, + /obj/random/unidentified_medicine/combat_medicine = 1, + /obj/random/tool/alien = 1, + /obj/random/handgun = 1, + /mob/living/simple_mob/animal/sif/hooligan_crab = 1 + )) + +#undef FISHING_RARE +#undef FISHING_UNCOMMON +#undef FISHING_COMMON +#undef FISHING_JUNK +#undef FISHING_NOTHING diff --git a/code/modules/fishing/fishing_net.dm b/code/modules/fishing/fishing_net.dm new file mode 100644 index 0000000000..740e3a2a6d --- /dev/null +++ b/code/modules/fishing/fishing_net.dm @@ -0,0 +1,109 @@ +/obj/item/weapon/material/fishing_net + name = "fishing net" + desc = "A crude fishing net." + icon = 'icons/obj/items.dmi' + icon_state = "net" + item_state = "net" + description_info = "This object can be used to capture certain creatures easily, most commonly fish. \ + It has a reach of two tiles, and can be emptied by activating it in-hand. \ + This version will not keep creatures inside in stasis, and will be heavier if it contains a mob." + + var/empty_state = "net" + var/contain_state = "net_full" + + w_class = ITEMSIZE_SMALL + flags = NOBLUDGEON + + slowdown = 0.25 + + reach = 2 + + default_material = "cloth" + + var/list/accepted_mobs = list(/mob/living/simple_mob/animal/passive/fish) + +/obj/item/weapon/material/fishing_net/Initialize() + ..() + update_icon() + +/obj/item/weapon/material/fishing_net/afterattack(var/atom/A, var/mob/user, var/proximity) + if(get_dist(get_turf(src), A) > reach) + return + + if(istype(A, /turf)) + var/mob/living/Target + for(var/type in accepted_mobs) + Target = locate(type) in A.contents + if(Target) + afterattack(Target, user, proximity) + break + + if(istype(A, /mob)) + var/accept = FALSE + for(var/D in accepted_mobs) + if(istype(A, D)) + accept = TRUE + for(var/atom/At in src.contents) + if(isliving(At)) + to_chat(user, "Your net is already holding something!") + accept = FALSE + if(!accept) + to_chat(user, "[A] can't be trapped in \the [src].") + return + var/mob/L = A + user.visible_message("[user] snatches [L] with \the [src].", "You snatch [L] with \the [src].") + L.forceMove(src) + update_icon() + update_weight() + return + return ..() + +/obj/item/weapon/material/fishing_net/attack_self(var/mob/user) + for(var/mob/M in src) + M.forceMove(get_turf(src)) + user.visible_message("[user] releases [M] from \the [src].", "You release [M] from \the [src].") + for(var/obj/item/I in src) + I.forceMove(get_turf(src)) + user.visible_message("[user] dumps \the [I] out of \the [src].", "You dump \the [I] out of \the [src].") + update_icon() + update_weight() + return + +/obj/item/weapon/material/fishing_net/attackby(var/obj/item/W, var/mob/user) + if(contents) + for(var/mob/living/L in contents) + if(prob(25)) + L.attackby(W, user) + ..() + +/obj/item/weapon/material/fishing_net/update_icon() // Also updates name and desc + underlays.Cut() + overlays.Cut() + + ..() + + name = initial(name) + desc = initial(desc) + var/contains_mob = FALSE + for(var/mob/M in src) + var/image/victim = image(M.icon, M.icon_state) + underlays += victim + name = "filled net" + desc = "A net with [M] inside." + contains_mob = TRUE + + if(contains_mob) + icon_state = contain_state + + else + icon_state = empty_state + + return + +/obj/item/weapon/material/fishing_net/proc/update_weight() + if(icon_state == contain_state) // Let's not do a for loop just to see if a mob is in here. + slowdown = initial(slowdown) * 2 + reach = 1 + else + slowdown = initial(slowdown) + reach = initial(reach) diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm new file mode 100644 index 0000000000..be7a6f6e79 --- /dev/null +++ b/code/modules/fishing/fishing_rod.dm @@ -0,0 +1,131 @@ + +/obj/item/weapon/material/fishing_rod + name = "crude fishing rod" + desc = "A crude rod made for catching fish." + description_info = "A tool usable on water-tiles to attempt to catch fish by swiping it over them.\ + You can add or remove cable by wirecutter or coil respectively to allow its use.\ + Any food containing things like protein, sugar, or standard nutriment can be attached to the rod, allowing for faster fishing based on the amount.\ + You can examine the rod to check if it has bait attached, and examine it automatically if so.\ + \ + Ctrl clicking the rod will remove any attached bait from the rod." + description_antag = "Some fishing rods can be utilized as long-range, sharp weapons, though their pseudo ranged ability comes at the cost of slow speed." + icon_state = "fishing_rod" + item_state = "fishing_rod" + force_divisor = 0.02 //VOREStation Edit + throwforce = 1 //VOREStation Edit + sharp = TRUE + attack_verb = list("whipped", "battered", "slapped", "fished", "hooked") + hitsound = 'sound/weapons/punchmiss.ogg' + applies_material_colour = TRUE + default_material = "wood" + can_dull = FALSE + var/strung = TRUE + var/line_break = TRUE + + var/obj/item/weapon/reagent_containers/food/snacks/Bait + var/bait_type = /obj/item/weapon/reagent_containers/food/snacks + + var/cast = FALSE + + attackspeed = 3 SECONDS + +/obj/item/weapon/material/fishing_rod/built + strung = FALSE + +/obj/item/weapon/material/fishing_rod/examine(mob/M as mob) + ..() + if(Bait) + to_chat(M, "\The [src] has \the [Bait] hanging on its hook.") + Bait.examine(M) + +/obj/item/weapon/material/fishing_rod/CtrlClick(mob/user) + if((src.loc == user || Adjacent(user)) && Bait) + Bait.forceMove(get_turf(user)) + to_chat(user, "You remove the bait from \the [src].") + Bait = null + else + ..() + +/obj/item/weapon/material/fishing_rod/Initialize() + ..() + update_icon() + +/obj/item/weapon/material/fishing_rod/attackby(obj/item/I as obj, mob/user as mob) + if(I.is_wirecutter() && strung) + strung = FALSE + to_chat(user, "You cut \the [src]'s string!") + update_icon() + return + else if(istype(I, /obj/item/stack/cable_coil) && !strung) + var/obj/item/stack/cable_coil/C = I + if(C.amount < 5) + to_chat(user, "You do not have enough length in \the [C] to string this!") + return + if(do_after(user, rand(10 SECONDS, 20 SECONDS))) + C.use(5) + strung = TRUE + to_chat(user, "You string \the [src]!") + update_icon() + return + else if(istype(I, bait_type)) + if(Bait) + Bait.forceMove(get_turf(user)) + to_chat(user, "You swap \the [Bait] with \the [I].") + Bait = I + user.drop_from_inventory(Bait) + Bait.forceMove(src) + update_bait() + return ..() + +/obj/item/weapon/material/fishing_rod/update_icon() + overlays.Cut() + ..() + if(strung) + overlays += image(icon, "[icon_state]_string") + +/obj/item/weapon/material/fishing_rod/proc/update_bait() + if(istype(Bait, bait_type)) + var/foodvolume + for(var/datum/reagent/re in Bait.reagents.reagent_list) + if(re.id == "nutriment" || re.id == "protein" || re.id == "glucose" || re.id == "fishbait") + foodvolume += re.volume + + toolspeed = initial(toolspeed) * min(0.75, (0.5 / max(0.5, (foodvolume / Bait.reagents.maximum_volume)))) + + else + toolspeed = initial(toolspeed) + +/obj/item/weapon/material/fishing_rod/proc/consume_bait() + if(Bait) + qdel(Bait) + Bait = null + return TRUE + return FALSE + +/obj/item/weapon/material/fishing_rod/attack(var/mob/M as mob, var/mob/user as mob, var/def_zone) + if(cast) + to_chat(user, "You cannot cast \the [src] when it is already in use!") + return FALSE + update_bait() + return ..() + +/obj/item/weapon/material/fishing_rod/modern + name = "fishing rod" + desc = "A refined rod for catching fish." + icon_state = "fishing_rod_modern" + item_state = "fishing_rod" + reach = 4 + attackspeed = 2 SECONDS + default_material = "titanium" + + toolspeed = 0.75 + +/obj/item/weapon/material/fishing_rod/modern/built + strung = FALSE + +/obj/item/weapon/material/fishing_rod/modern/cheap //A rod sold by the fishing vendor. Done so that the rod sold by mining reward vendors doesn't loose its value. + name = "cheap fishing rod" + desc = "Mass produced, but somewhat reliable." + default_material = "plastic" + + toolspeed = 0.9 \ No newline at end of file diff --git a/code/modules/fishing/fishing_rod_vr.dm b/code/modules/fishing/fishing_rod_vr.dm new file mode 100644 index 0000000000..e802507b9b --- /dev/null +++ b/code/modules/fishing/fishing_rod_vr.dm @@ -0,0 +1,5 @@ +/obj/item/weapon/material/fishing_rod/modern/strong + desc = "A extremely refined rod for catching fish." + default_material = "durasteel" + + toolspeed = 0.5 \ No newline at end of file diff --git a/code/modules/fishing/fishing_vr.dm b/code/modules/fishing/fishing_vr.dm new file mode 100644 index 0000000000..e545cc0edf --- /dev/null +++ b/code/modules/fishing/fishing_vr.dm @@ -0,0 +1,42 @@ +#define FISHING_RARE "rare" +#define FISHING_UNCOMMON "uncommon" +#define FISHING_COMMON "common" +#define FISHING_JUNK "junk" +#define FISHING_NOTHING "nothing" + +GLOBAL_LIST_INIT(indoor_fishing_chance_list, list(FISHING_RARE = 5, FISHING_UNCOMMON = 20, FISHING_COMMON = 30, FISHING_JUNK = 5, FISHING_NOTHING = 50)) +GLOBAL_LIST_INIT(indoor_fishing_junk_list, list( + /obj/random/junk = 15, + /obj/random/maintenance/clean = 1 + )) + +/turf/simulated/floor/water/indoors + min_fishing_time = 33 + max_fishing_time = 99 + +/turf/simulated/floor/water/indoors/handle_fish() + if(has_fish) + rare_fish_list = GLOB.generic_fishing_rare_list + uncommon_fish_list = GLOB.generic_fishing_uncommon_list + common_fish_list = GLOB.generic_fishing_common_list + junk_list = GLOB.indoor_fishing_junk_list + fishing_loot = GLOB.indoor_fishing_chance_list + +/turf/simulated/floor/water/deep/indoors + min_fishing_time = 33 + max_fishing_time = 99 + +/turf/simulated/floor/water/deep/indoors/handle_fish() + if(has_fish) + rare_fish_list = GLOB.generic_fishing_rare_list + uncommon_fish_list = GLOB.generic_fishing_uncommon_list + common_fish_list = GLOB.generic_fishing_common_list + junk_list = GLOB.indoor_fishing_junk_list + fishing_loot = GLOB.indoor_fishing_chance_list + + +#undef FISHING_RARE +#undef FISHING_UNCOMMON +#undef FISHING_COMMON +#undef FISHING_JUNK +#undef FISHING_NOTHING \ No newline at end of file diff --git a/code/modules/food/drinkingglass/metaglass.dm b/code/modules/food/drinkingglass/metaglass.dm index babb71df8b..1980c58873 100644 --- a/code/modules/food/drinkingglass/metaglass.dm +++ b/code/modules/food/drinkingglass/metaglass.dm @@ -312,7 +312,7 @@ Drinks Data glass_icon_state = "atomicbombglass" glass_center_of_mass = list("x"=15, "y"=7) -/datum/reagent/ethanol/b52 +/datum/reagent/ethanol/coffee/b52 glass_icon_state = "b52glass" /datum/reagent/ethanol/bahama_mama @@ -546,3 +546,7 @@ Drinks Data /datum/reagent/drink/eggnog glass_icon_state = "eggnog" glass_center_of_mass = list("x"=16, "y"=8) + +/datum/reagent/drink/cider + glass_icon_state = "ciderglass" + glass_center_of_mass = list("x"=16, "y"=8) diff --git a/code/modules/food/food/condiment.dm b/code/modules/food/food/condiment.dm index 377679f778..522f79b89b 100644 --- a/code/modules/food/food/condiment.dm +++ b/code/modules/food/food/condiment.dm @@ -126,6 +126,26 @@ . = ..() reagents.add_reagent("sugar", 50) +/obj/item/weapon/reagent_containers/food/condiment/ketchup/Initialize() + . = ..() + reagents.add_reagent("ketchup", 50) + +/obj/item/weapon/reagent_containers/food/condiment/hotsauce/Initialize() + . = ..() + reagents.add_reagent("capsaicin", 50) + +/obj/item/weapon/reagent_containers/food/condiment/cornoil/Initialize() + . = ..() + reagents.add_reagent("cornoil", 50) + +/obj/item/weapon/reagent_containers/food/condiment/coldsauce/Initialize() + . = ..() + reagents.add_reagent("frostoil", 50) + +/obj/item/weapon/reagent_containers/food/condiment/soysauce/Initialize() + . = ..() + reagents.add_reagent("soysauce", 50) + /obj/item/weapon/reagent_containers/food/condiment/small possible_transfer_amounts = list(1,20) amount_per_transfer_from_this = 1 @@ -162,6 +182,198 @@ . = ..() reagents.add_reagent("sugar", 20) +//MRE condiments and drinks. + +/obj/item/weapon/reagent_containers/food/condiment/small/packet + icon_state = "packet_small" + w_class = ITEMSIZE_TINY + possible_transfer_amounts = "1;5;10" + amount_per_transfer_from_this = 1 + volume = 5 + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/salt + name = "salt packet" + desc = "Contains 5u of table salt." + icon_state = "packet_small_white" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/salt/Initialize() + . = ..() + reagents.add_reagent("sodiumchloride", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/pepper + name = "pepper packet" + desc = "Contains 5u of black pepper." + icon_state = "packet_small_black" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/pepper/Initialize() + . = ..() + reagents.add_reagent("blackpepper", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/sugar + name = "sugar packet" + desc = "Contains 5u of refined sugar." + icon_state = "packet_small_white" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/sugar/Initialize() + . = ..() + reagents.add_reagent("sugar", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/jelly + name = "jelly packet" + desc = "Contains 10u of cherry jelly. Best used for spreading on crackers." + icon_state = "packet_medium" + volume = 10 + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/jelly/Initialize() + . = ..() + reagents.add_reagent("cherryjelly", 10) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/honey + name = "honey packet" + desc = "Contains 10u of honey." + icon_state = "packet_medium" + volume = 10 + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/honey/Initialize() + . = ..() + reagents.add_reagent("honey", 10) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/capsaicin + name = "hot sauce packet" + desc = "Contains 5u of hot sauce. Enjoy in moderation." + icon_state = "packet_small_red" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/capsaicin/Initialize() + . = ..() + reagents.add_reagent("capsaicin", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/ketchup + name = "ketchup packet" + desc = "Contains 5u of ketchup." + icon_state = "packet_small_red" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/ketchup/Initialize() + . = ..() + reagents.add_reagent("ketchup", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/mayo + name = "mayonnaise packet" + desc = "Contains 5u of mayonnaise." + icon_state = "packet_small_white" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/mayo/Initialize() + . = ..() + reagents.add_reagent("mayo", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/soy + name = "soy sauce packet" + desc = "Contains 5u of soy sauce." + icon_state = "packet_small_black" + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/soy/Initialize() + . = ..() + reagents.add_reagent("soysauce", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/coffee + name = "coffee powder packet" + desc = "Contains 5u of coffee powder. Mix with 25u of water and heat." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/coffee/Initialize() + . = ..() + reagents.add_reagent("coffeepowder", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/tea + name = "tea powder packet" + desc = "Contains 5u of black tea powder. Mix with 25u of water and heat." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/tea/Initialize() + . = ..() + reagents.add_reagent("tea", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/cocoa + name = "cocoa powder packet" + desc = "Contains 5u of cocoa powder. Mix with 25u of water and heat." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/cocoa/Initialize() + . = ..() + reagents.add_reagent("coco", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/grape + name = "grape juice powder packet" + desc = "Contains 5u of powdered grape juice. Mix with 15u of water." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/grape/Initialize() + . = ..() + reagents.add_reagent("instantgrape", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/orange + name = "orange juice powder packet" + desc = "Contains 5u of powdered orange juice. Mix with 15u of water." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/orange/Initialize() + . = ..() + reagents.add_reagent("instantorange", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/watermelon + name = "watermelon juice powder packet" + desc = "Contains 5u of powdered watermelon juice. Mix with 15u of water." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/watermelon/Initialize() + . = ..() + reagents.add_reagent("instantwatermelon", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/apple + name = "apple juice powder packet" + desc = "Contains 5u of powdered apple juice. Mix with 15u of water." + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/apple/Initialize() + . = ..() + reagents.add_reagent("instantapple", 5) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/protein + name = "protein powder packet" + desc = "Contains 10u of powdered protein. Mix with 20u of water." + icon_state = "packet_medium" + volume = 10 + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/protein/Initialize() + . = ..() + reagents.add_reagent("protein", 10) + +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon + name = "crayon powder packet" + desc = "Contains 10u of powdered crayon. Mix with 30u of water." + volume = 10 +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/generic/Initialize() + . = ..() + reagents.add_reagent("crayon_dust", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/red/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_red", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/orange/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_orange", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/yellow/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_yellow", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/green/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_green", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/blue/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_blue", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/purple/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_purple", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/grey/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_grey", 10) +/obj/item/weapon/reagent_containers/food/condiment/small/packet/crayon/brown/Initialize() + . = ..() + reagents.add_reagent("crayon_dust_brown", 10) + +//End of MRE stuff. + /obj/item/weapon/reagent_containers/food/condiment/flour name = "flour sack" desc = "A big bag of flour. Good for baking!" diff --git a/code/modules/food/food/drinks/bottle.dm b/code/modules/food/food/drinks/bottle.dm index d2506132fd..0a56f946d5 100644 --- a/code/modules/food/food/drinks/bottle.dm +++ b/code/modules/food/food/drinks/bottle.dm @@ -178,6 +178,7 @@ throw_speed = 3 throw_range = 5 item_state = "beer" + flags = NOCONDUCT attack_verb = list("stabbed", "slashed", "attacked") sharp = 1 edge = 0 @@ -520,6 +521,17 @@ . = ..() reagents.add_reagent("beer", 30) +/obj/item/weapon/reagent_containers/food/drinks/bottle/small/cider + name = "Crisp's Cider" + desc = "Fermented apples never tasted this good." + icon_state = "cider" + center_of_mass = list("x"=16, "y"=12) + +/obj/item/weapon/reagent_containers/food/drinks/bottle/small/cider/Initialize() + . = ..() + reagents.add_reagent("cider", 30) + + /obj/item/weapon/reagent_containers/food/drinks/bottle/small/ale name = "\improper Magm-Ale" desc = "A true dorf's drink of choice." diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index f0840919e2..eab6c1b683 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -11,21 +11,17 @@ var/slices_num var/dried_type = null var/dry = 0 + var/survivalfood = FALSE var/nutriment_amt = 0 var/list/nutriment_desc = list("food" = 1) center_of_mass = list("x"=16, "y"=16) w_class = ITEMSIZE_SMALL - force = 1 + force = 0 /obj/item/weapon/reagent_containers/food/snacks/Initialize() . = ..() if(nutriment_amt) - reagents.add_reagent("nutriment",nutriment_amt,nutriment_desc) - -/obj/item/weapon/reagent_containers/food/snacks/Initialize() - . = ..() - if(nutriment_amt) - reagents.add_reagent("nutriment", nutriment_amt) + reagents.add_reagent("nutriment",(nutriment_amt*2),nutriment_desc) //Placeholder for effect that trigger on eating that aren't tied to reagents. /obj/item/weapon/reagent_containers/food/snacks/proc/On_Consume(var/mob/M) @@ -49,7 +45,7 @@ /obj/item/weapon/reagent_containers/food/snacks/attack(mob/M as mob, mob/user as mob, def_zone) if(reagents && !reagents.total_volume) - user << "None of [src] left!" + to_chat(user, "None of [src] left!") user.drop_from_inventory(src) qdel(src) return 0 @@ -62,38 +58,42 @@ if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M if(!H.check_has_mouth()) - user << "Where do you intend to put \the [src]? You don't have a mouth!" + to_chat(user, "Where do you intend to put \the [src]? You don't have a mouth!") return - var/obj/item/blocked = H.check_mouth_coverage() + var/obj/item/blocked = null + if(survivalfood) + blocked = H.check_mouth_coverage_survival() + else + blocked = H.check_mouth_coverage() if(blocked) - user << "\The [blocked] is in the way!" + to_chat(user, "\The [blocked] is in the way!") return user.setClickCooldown(user.get_attack_speed(src)) //puts a limit on how fast people can eat/drink things //VOREStation Edit Begin if (fullness <= 50) - M << "You hungrily chew out a piece of [src] and gobble it!" + to_chat(M, "You hungrily chew out a piece of [src] and gobble it!") if (fullness > 50 && fullness <= 150) - M << "You hungrily begin to eat [src]." + to_chat(M, "You hungrily begin to eat [src].") if (fullness > 150 && fullness <= 350) - M << "You take a bite of [src]." + to_chat(M, "You take a bite of [src].") if (fullness > 350 && fullness <= 550) - M << "You unwillingly chew a bit of [src]." + to_chat(M, "You unwillingly chew a bit of [src].") if (fullness > 550 && fullness <= 650) - M << "You swallow some more of the [src], causing your belly to swell out a little." + to_chat(M, "You swallow some more of the [src], causing your belly to swell out a little.") if (fullness > 650 && fullness <= 1000) - M << "You stuff yourself with the [src]. Your stomach feels very heavy." + to_chat(M, "You stuff yourself with the [src]. Your stomach feels very heavy.") if (fullness > 1000 && fullness <= 3000) - M << "You gluttonously swallow down the hunk of [src]. You're so gorged, it's hard to stand." + to_chat(M, "You gluttonously swallow down the hunk of [src]. You're so gorged, it's hard to stand.") if (fullness > 3000 && fullness <= 5500) - M << "You force the piece of [src] down your throat. You can feel your stomach getting firm as it reaches its limits." + to_chat(M, "You force the piece of [src] down your throat. You can feel your stomach getting firm as it reaches its limits.") if (fullness > 5500 && fullness <= 6000) - M << "You barely glug down the bite of [src], causing undigested food to force into your intestines. You can't take much more of this!" + to_chat(M, "You barely glug down the bite of [src], causing undigested food to force into your intestines. You can't take much more of this!") if (fullness > 6000) // There has to be a limit eventually. - M << "Your stomach blorts and aches, prompting you to stop. You literally cannot force any more of [src] to go down your throat." + to_chat(M, "Your stomach blorts and aches, prompting you to stop. You literally cannot force any more of [src] to go down your throat.") return 0 /*if (fullness > (550 * (1 + M.overeatduration / 2000))) // The more you eat - the more you can eat - M << "You cannot force any more of [src] to go down your throat." + to_chat(M, "You cannot force any more of [src] to go down your throat.") return 0*/ //VOREStation Edit End @@ -104,11 +104,23 @@ if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M if(!H.check_has_mouth()) - user << "Where do you intend to put \the [src]? \The [H] doesn't have a mouth!" + to_chat(user, "Where do you intend to put \the [src]? \The [H] doesn't have a mouth!") return - var/obj/item/blocked = H.check_mouth_coverage() + var/obj/item/blocked = null + var/unconcious = FALSE + blocked = H.check_mouth_coverage() + if(survivalfood) + blocked = H.check_mouth_coverage_survival() + if(H.stat && H.check_mouth_coverage()) + unconcious = TRUE + blocked = H.check_mouth_coverage() + + if(unconcious) + to_chat(user, "You can't feed [H] through \the [blocked] while they are unconcious!") + return + if(blocked) - user << "\The [blocked] is in the way!" + to_chat(user, "\The [blocked] is in the way!") return /*if (fullness <= (550 * (1 + M.overeatduration / 1000))) // Vorestation edit @@ -127,7 +139,7 @@ user.visible_message("[user] feeds [M] [src].") else - user << "This creature does not seem to have a mouth!" + to_chat(user, "This creature does not seem to have a mouth!") return if(reagents) //Handle ingestion of the reagent. @@ -149,11 +161,11 @@ if (bitecount==0) return else if (bitecount==1) - user << "\The [src] was bitten by someone!" + to_chat(user, "\The [src] was bitten by someone!") else if (bitecount<=3) - user << "\The [src] was bitten [bitecount] times!" + to_chat(user, "\The [src] was bitten [bitecount] times!") else - user << "\The [src] was bitten multiple times!" + to_chat(user, "\The [src] was bitten multiple times!") /obj/item/weapon/reagent_containers/food/snacks/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/weapon/storage)) @@ -168,7 +180,7 @@ U.create_reagents(5) if (U.reagents.total_volume > 0) - user << "You already have something on your [U]." + to_chat(user, "You already have something on your [U].") return user.visible_message( \ @@ -698,18 +710,36 @@ bitesize = 1 /obj/item/weapon/reagent_containers/food/snacks/carpmeat - name = "carp fillet" - desc = "A fillet of spess carp meat" + name = "fillet" + desc = "A fillet of carp meat" icon_state = "fishfillet" filling_color = "#FFDEFE" center_of_mass = list("x"=17, "y"=13) + var/toxin_type = "carpotoxin" + var/toxin_amount = 3 + /obj/item/weapon/reagent_containers/food/snacks/carpmeat/Initialize() . = ..() reagents.add_reagent("protein", 3) - reagents.add_reagent("carpotoxin", 3) + reagents.add_reagent(toxin_type, toxin_amount) src.bitesize = 6 +/obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif + desc = "A fillet of sivian fish meat." + filling_color = "#2c2cff" + color = "#2c2cff" + toxin_type = "neurotoxic_protein" + toxin_amount = 2 + +/obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish + toxin_type = "murk_protein" + +/obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish + desc = "A fillet of fish meat." + toxin_type = "neurotoxic_protein" + toxin_amount = 1 + /obj/item/weapon/reagent_containers/food/snacks/fishfingers name = "Fish Fingers" desc = "A finger of fish." @@ -720,7 +750,18 @@ /obj/item/weapon/reagent_containers/food/snacks/fishfingers/Initialize() . = ..() reagents.add_reagent("protein", 4) - reagents.add_reagent("carpotoxin", 3) + bitesize = 3 + +/obj/item/weapon/reagent_containers/food/snacks/zestfish + name = "Zesty Fish" + desc = "Lightly seasoned fish fillets." + icon_state = "zestfish" + filling_color = "#FFDEFE" + center_of_mass = list("x"=16, "y"=13) + +/obj/item/weapon/reagent_containers/food/snacks/zestfish/Initialize() + . = ..() + reagents.add_reagent("protein", 4) bitesize = 3 /obj/item/weapon/reagent_containers/food/snacks/hugemushroomslice @@ -942,7 +983,6 @@ /obj/item/weapon/reagent_containers/food/snacks/fishburger/Initialize() . = ..() reagents.add_reagent("protein", 6) - reagents.add_reagent("carpotoxin", 3) bitesize = 3 /obj/item/weapon/reagent_containers/food/snacks/tofuburger @@ -1281,7 +1321,6 @@ /obj/item/weapon/reagent_containers/food/snacks/cubancarp/Initialize() . = ..() reagents.add_reagent("protein", 3) - reagents.add_reagent("carpotoxin", 3) reagents.add_reagent("capsaicin", 3) bitesize = 3 @@ -1942,7 +1981,6 @@ /obj/item/weapon/reagent_containers/food/snacks/fishandchips/Initialize() . = ..() reagents.add_reagent("protein", 3) - reagents.add_reagent("carpotoxin", 3) bitesize = 3 /obj/item/weapon/reagent_containers/food/snacks/sandwich @@ -2177,6 +2215,21 @@ . = ..() bitesize = 2 +/obj/item/weapon/reagent_containers/food/snacks/kudzudonburi + name = "Zhan-Kudzu Overtaker" + desc = "Seasoned Kudzu and fish donburi." + icon_state = "kudzudonburi" + trash = /obj/item/trash/snack_bowl + filling_color = "#FFFBDB" + center_of_mass = list("x"=17, "y"=11) + nutriment_amt = 16 + nutriment_desc = list("rice" = 2, "gauze" = 4, "fish" = 10) + +/obj/item/weapon/reagent_containers/food/snacks/kudzudonburi/Initialize() + . = ..() + reagents.add_reagent("protein", 4) + bitesize = 2 + /obj/item/weapon/reagent_containers/food/snacks/pastatomato name = "Spaghetti" desc = "Spaghetti and crushed tomatoes. Just like your abusive father used to make!" @@ -2324,6 +2377,14 @@ . = ..() reagents.add_reagent("cherryjelly", 5) +/obj/item/weapon/reagent_containers/food/snacks/jellysandwich/peanutbutter + desc = "You wish you had some peanut butter to go with this... Oh wait!" + icon_state = "pbandj" + +/obj/item/weapon/reagent_containers/food/snacks/jellysandwich/peanutbutter/Initialize() + . = ..() + reagents.add_reagent("peanutbutter", 5) + /obj/item/weapon/reagent_containers/food/snacks/boiledslimecore name = "Boiled slime Core" desc = "A boiled red thing." @@ -2694,6 +2755,35 @@ /obj/item/weapon/reagent_containers/food/snacks/slice/cheesecake/filled filled = TRUE +/obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake + name = "Peanut Cake" + desc = "DANGEROUSLY nutty. Sometimes literally." + icon_state = "peanutcake" + slice_path = /obj/item/weapon/reagent_containers/food/snacks/slice/peanutcake + slices_num = 5 + filling_color = "#4F3500" + center_of_mass = list("x"=16, "y"=10) + nutriment_desc = list("cake" = 10, "peanuts" = 15) + nutriment_amt = 10 + +/obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake/Initialize() + . = ..() + reagents.add_reagent("protein", 5) + bitesize = 2 + +/obj/item/weapon/reagent_containers/food/snacks/slice/peanutcake + name = "Peanut Cake slice" + desc = "Slice of nutty goodness." + icon_state = "peanutcake_slice" + trash = /obj/item/trash/plate + filling_color = "#4F3500" + bitesize = 2 + center_of_mass = list("x"=16, "y"=14) + whole_path = /obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake + +/obj/item/weapon/reagent_containers/food/snacks/slice/peanutcake/filled + filled = TRUE + /obj/item/weapon/reagent_containers/food/snacks/sliceable/plaincake name = "Vanilla Cake" desc = "A plain cake, not a lie." @@ -2993,6 +3083,7 @@ filling_color = "#F5DEB8" center_of_mass = list("x"=16, "y"=6) nutriment_desc = list("salt" = 1, "cracker" = 2) + w_class = ITEMSIZE_TINY nutriment_amt = 1 @@ -3031,6 +3122,34 @@ /obj/item/weapon/reagent_containers/food/snacks/slice/margherita/filled filled = TRUE +/obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple + name = "Hawaiian" + desc = "The accursed wheel from ancient times." + icon_state = "pizzamargherita" + slice_path = /obj/item/weapon/reagent_containers/food/snacks/slice/pineapple + slices_num = 6 + center_of_mass = list("x"=16, "y"=11) + nutriment_desc = list("pizza crust" = 5, "tomato" = 5, "cheese" = 5, "pineapple" = 20) + nutriment_amt = 35 + +/obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple/Initialize() + . = ..() + reagents.add_reagent("protein", 5) + reagents.add_reagent("pineapplejuice", 6) + bitesize = 2 + +/obj/item/weapon/reagent_containers/food/snacks/slice/pineapple + name = "Hawaiian slice" + desc = "A slice of the accursed pizza." + icon_state = "pizzamargheritaslice" + filling_color = "#BAA14C" + bitesize = 2 + center_of_mass = list("x"=16, "y"=13) + whole_path = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple + +/obj/item/weapon/reagent_containers/food/snacks/slice/pineapple/filled + filled = TRUE + /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza name = "Meatpizza" desc = "A pizza with meat topping." @@ -3467,6 +3586,7 @@ center_of_mass = list("x"=21, "y"=12) nutriment_amt = 4 nutriment_desc = list("cheese" = 2,"taco shell" = 2) + /obj/item/weapon/reagent_containers/food/snacks/taco/Initialize() . = ..() reagents.add_reagent("protein", 3) @@ -3543,7 +3663,11 @@ /obj/item/weapon/reagent_containers/food/snacks/grown/attackby(obj/item/weapon/W, mob/user) if(seed && seed.kitchen_tag && seed.kitchen_tag == "potato" && istype(W,/obj/item/weapon/material/knife)) new /obj/item/weapon/reagent_containers/food/snacks/rawsticks(get_turf(src)) - user << "You cut the potato." + to_chat(user, "You cut the potato.") + qdel(src) + else if(seed && seed.kitchen_tag && seed.kitchen_tag == "sunflower" && istype(W,/obj/item/weapon/material/knife)) + new /obj/item/weapon/reagent_containers/food/snacks/rawsunflower(get_turf(src)) + to_chat(user, "You remove the seeds from the flower, slightly damaging them.") qdel(src) else ..() @@ -3561,12 +3685,46 @@ /obj/item/weapon/reagent_containers/food/snacks/rawsticks/Initialize() . = ..() +/obj/item/weapon/reagent_containers/food/snacks/rawsunflower + name = "sunflower seeds" + desc = "Raw sunflower seeds, alright. They look too damaged to plant." + icon = 'icons/obj/food_ingredients.dmi' + icon_state = "sunflowerseed" + bitesize = 1 + center_of_mass = list("x"=17, "y"=18) + nutriment_amt = 1 + nutriment_desc = list("starch" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/rawsunflower/Initialize() + . = ..() + +/obj/item/weapon/reagent_containers/food/snacks/roastedsunflower + name = "sunflower seeds" + desc = "Sunflower seeds!" + icon = 'icons/obj/food.dmi' + icon_state = "sunflowerseed" + bitesize = 1 + center_of_mass = list("x"=15, "y"=17) + nutriment_amt = 2 + nutriment_desc = list("salt" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/roastedpeanuts + name = "peanuts" + desc = "Stopped being the planetary airline food of Earth in 2120." + icon = 'icons/obj/food.dmi' + icon_state = "roastnuts" + bitesize = 1 + center_of_mass = list("x"=15, "y"=17) + nutriment_amt = 2 + nutriment_desc = list("salt" = 3) + /obj/item/weapon/reagent_containers/food/snacks/liquidfood name = "\improper LiquidFood Ration" desc = "A prepackaged grey slurry of all the essential nutrients for a spacefarer on the go. Should this be crunchy?" icon_state = "liquidfood" trash = /obj/item/trash/liquidfood filling_color = "#A8A8A8" + survivalfood = TRUE center_of_mass = list("x"=16, "y"=15) nutriment_amt = 20 nutriment_desc = list("chalk" = 6) @@ -3576,6 +3734,51 @@ reagents.add_reagent("iron", 3) bitesize = 4 +/obj/item/weapon/reagent_containers/food/snacks/liquidprotein + name = "\improper LiquidProtein Ration" + desc = "A variant of the liquidfood ration, designed for obligate carnivore species. Only barely more appealing than regular liquidfood. Should this be crunchy?" + icon_state = "liquidprotein" + trash = /obj/item/trash/liquidprotein + filling_color = "#A8A8A8" + survivalfood = TRUE + center_of_mass = list("x"=16, "y"=15) + +/obj/item/weapon/reagent_containers/food/snacks/liquidprotein/Initialize() + ..() + reagents.add_reagent("protein", 30) + reagents.add_reagent("iron", 3) + bitesize = 4 + +/obj/item/weapon/reagent_containers/food/snacks/liquidvitamin + name = "\improper VitaPaste Ration" + desc = "A variant of the liquidfood ration, designed for any carbon-based life. Somehow worse than regular liquidfood. Should this be crunchy?" + icon_state = "liquidvitamin" + trash = /obj/item/trash/liquidvitamin + filling_color = "#A8A8A8" + survivalfood = TRUE + center_of_mass = list("x"=16, "y"=15) + +/obj/item/weapon/reagent_containers/food/snacks/liquidvitamin/Initialize() + ..() + reagents.add_reagent("flour", 20) + reagents.add_reagent("tricordrazine", 5) + reagents.add_reagent("paracetamol", 5) + reagents.add_reagent("enzyme", 1) + reagents.add_reagent("iron", 3) + bitesize = 4 + +/obj/item/weapon/reagent_containers/food/snacks/meatcube + name = "cubed meat" + desc = "Fried, salted lean meat compressed into a cube. Not very appetizing." + icon_state = "meatcube" + filling_color = "#7a3d11" + center_of_mass = list("x"=16, "y"=16) + +/obj/item/weapon/reagent_containers/food/snacks/meatcube/Initialize() + . = ..() + reagents.add_reagent("protein", 15) + bitesize = 3 + /obj/item/weapon/reagent_containers/food/snacks/tastybread name = "bread tube" desc = "Bread in a tube. Chewy...and surprisingly tasty." @@ -3643,7 +3846,7 @@ /obj/item/weapon/reagent_containers/food/snacks/sashimi name = "carp sashimi" - desc = "Expertly prepared. Still toxic." + desc = "Expertly prepared. Hopefully toxin got removed though." filling_color = "#FFDEFE" icon_state = "sashimi" nutriment_amt = 6 @@ -3651,7 +3854,6 @@ /obj/item/weapon/reagent_containers/food/snacks/sashimi/Initialize() . = ..() reagents.add_reagent("protein", 2) - reagents.add_reagent("carpotoxin", 2) bitesize = 3 /obj/item/weapon/reagent_containers/food/snacks/benedict @@ -3733,6 +3935,21 @@ reagents.add_reagent("protein", 2) bitesize = 2 +/obj/item/weapon/reagent_containers/food/snacks/devilledegg + name = "devilled eggs" + desc = "Spicy homestyle favorite." + icon_state = "devilledegg" + filling_color = "#799ACE" + center_of_mass = list("x"=17, "y"=16) + nutriment_amt = 8 + nutriment_desc = list("egg" = 4, "chili" = 4) + +/obj/item/weapon/reagent_containers/food/snacks/devilledegg/Initialize() + . = ..() + reagents.add_reagent("nutriment", 6) + reagents.add_reagent("capsaicin", 2) + bitesize = 2 + /obj/item/weapon/reagent_containers/food/snacks/fruitsalad name = "fruit salad" desc = "Your standard fruit salad." @@ -3746,6 +3963,33 @@ reagents.add_reagent("nutriment", 10) bitesize = 4 +/obj/item/weapon/reagent_containers/food/snacks/flowerchildsalad + name = "flowerchild salad" + desc = "A fragrant salad." + icon_state = "flowerchildsalad" + filling_color = "#FF3867" + nutriment_amt = 10 + nutriment_desc = list("bittersweet" = 10) + +/obj/item/weapon/reagent_containers/food/snacks/flowerchildsalad/Initialize() + . = ..() + reagents.add_reagent("nutriment", 10) + bitesize = 4 + +/obj/item/weapon/reagent_containers/food/snacks/rosesalad + name = "flowerchild salad" + desc = "A fragrant salad." + icon_state = "rosesalad" + filling_color = "#FF3867" + nutriment_amt = 5 + nutriment_desc = list("bittersweet" = 10, "iron" = 5) + +/obj/item/weapon/reagent_containers/food/snacks/rosesalad/Initialize() + . = ..() + reagents.add_reagent("nutriment", 10) + reagents.add_reagent("stoxin", 2) + bitesize = 4 + /obj/item/weapon/reagent_containers/food/snacks/eggbowl name = "egg bowl" desc = "A bowl of fried rice with egg mixed in." @@ -3864,6 +4108,19 @@ reagents.add_reagent("capsaicin", 4) bitesize = 2 +/obj/item/weapon/reagent_containers/food/snacks/curryrice + name = "curry rice" + desc = "That's some dangerously spicy rice." + icon_state = "curryrice" + nutriment_amt = 6 + nutriment_desc = list("salt" = 1, "rice" = 2, "chili peppers" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/curryrice/Initialize() + . = ..() + reagents.add_reagent("nutriment", 5) + reagents.add_reagent("capsaicin", 4) + bitesize = 2 + /obj/item/weapon/reagent_containers/food/snacks/piginblanket name = "pig in a blanket" desc = "A sausage embedded in soft, fluffy pastry. Free this pig from its blanket prison by eating it." @@ -3875,4 +4132,79 @@ . = ..() reagents.add_reagent("nutriment", 6) reagents.add_reagent("protein", 4) - bitesize = 3 \ No newline at end of file + bitesize = 3 + +/obj/item/weapon/reagent_containers/food/snacks/piginblanket + name = "pig in a blanket" + desc = "A sausage embedded in soft, fluffy pastry. Free this pig from its blanket prison by eating it." + icon_state = "piginblanket" + nutriment_amt = 6 + nutriment_desc = list("meat" = 3, "pastry" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/piginblanket/Initialize() + . = ..() + reagents.add_reagent("nutriment", 6) + reagents.add_reagent("protein", 4) + bitesize = 3 + +/obj/item/weapon/reagent_containers/food/snacks/wormsickly + name = "sickly worm" + desc = "A worm, it doesn't look particularily healthy, but it will still serve as good fishing bait." + icon_state = "worm_sickly" + nutriment_amt = 1 + nutriment_desc = list("bugflesh" = 1) + w_class = ITEMSIZE_TINY + +/obj/item/weapon/reagent_containers/food/snacks/wormsickly/Initialize() + . = ..() + reagents.add_reagent("fishbait", 10) + bitesize = 5 + +/obj/item/weapon/reagent_containers/food/snacks/worm + name = "strange worm" + desc = "A peculiar worm, freshly plucked from the earth." + icon_state = "worm" + nutriment_amt = 1 + nutriment_desc = list("bugflesh" = 1) + w_class = ITEMSIZE_TINY + +/obj/item/weapon/reagent_containers/food/snacks/worm/Initialize() + . = ..() + reagents.add_reagent("fishbait", 20) + bitesize = 5 + +/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe + name = "deluxe worm" + desc = "A fancy worm, genetically engineered to appeal to fish." + icon_state = "worm_deluxe" + nutriment_amt = 5 + nutriment_desc = list("bugflesh" = 1) + w_class = ITEMSIZE_TINY + +/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe/Initialize() + . = ..() + reagents.add_reagent("fishbait", 40) + bitesize = 5 + +/obj/item/weapon/reagent_containers/food/snacks/siffruit + name = "pulsing fruit" + desc = "A blue-ish sac encased in a tough black shell." + icon = 'icons/obj/flora/foraging.dmi' + icon_state = "siffruit" + nutriment_amt = 2 + nutriment_desc = list("tart" = 1) + w_class = ITEMSIZE_TINY + +/obj/item/weapon/reagent_containers/food/snacks/siffruit/Initialize() + . = ..() + reagents.add_reagent("sifsap", 2) + +/obj/item/weapon/reagent_containers/food/snacks/siffruit/afterattack(obj/O as obj, mob/user as mob, proximity) + if(istype(O,/obj/machinery/microwave)) + return ..() + if(!(proximity && O.is_open_container())) + return + to_chat(user, "You tear \the [src]'s sac open, pouring it into \the [O].") + reagents.trans_to(O, reagents.total_volume) + user.drop_from_inventory(src) + qdel(src) diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm index 52f3752095..a283a6bafe 100644 --- a/code/modules/food/food/snacks_vr.dm +++ b/code/modules/food/food/snacks_vr.dm @@ -277,6 +277,113 @@ reagents.add_reagent("shockchem", 6) bitesize = 7 +/obj/item/weapon/reagent_containers/food/snacks/lobster + name = "raw lobster" + desc = "a shifty lobster. You can try eating it, but its shell is extremely tough." + icon = 'icons/obj/food_vr.dmi' + icon_state = "lobster_raw" + nutriment_amt = 5 + +/obj/item/weapon/reagent_containers/food/snacks/lobster/Initialize() + ..() + bitesize = 0.1 + +/obj/item/weapon/reagent_containers/food/snacks/lobstercooked + name = "cooked lobster" + desc = "a luxurious plate of cooked lobster, its taste accentuated by lemon juice. Reinvigorating!" + icon = 'icons/obj/food_vr.dmi' + icon_state = "lobster_cooked" + trash = /obj/item/trash/plate + nutriment_amt = 20 + nutriment_desc = list("lemon" = 2, "lobster" = 5, "salad" = 2) + +/obj/item/weapon/reagent_containers/food/snacks/lobstercooked/Initialize() + ..() + bitesize = 5 + reagents.add_reagent("protein", 20) + reagents.add_reagent("tricordrazine", 5) + reagents.add_reagent("iron", 5) + +/obj/item/weapon/reagent_containers/food/snacks/cuttlefish + name = "raw cuttlefish" + desc = "it's an adorable squid! you can't possible be thinking about eating this right?" + icon = 'icons/obj/food_vr.dmi' + icon_state = "cuttlefish_raw" + nutriment_amt = 5 + +/obj/item/weapon/reagent_containers/food/snacks/cuttlefish/Initialize() + ..() + bitesize = 10 + +/obj/item/weapon/reagent_containers/food/snacks/cuttlefishcooked + name = "cooked cuttlefish" + desc = "it's a roasted cuttlefish. rubbery, squishy, an acquired taste." + icon = 'icons/obj/food_vr.dmi' + icon_state = "cuttlefish_cooked" + nutriment_amt = 20 + nutriment_desc = list("cuttlefish" = 5, "rubber" = 5, "grease" = 1) + +/obj/item/weapon/reagent_containers/food/snacks/cuttlefishcooked/Initialize() + ..() + bitesize = 5 + reagents.add_reagent("protein", 10) + +/obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfish + name = "extra large monkfish" + desc = "it's a huge monkfish. better clean it first, you can't possibly eat it like this." + icon = 'icons/obj/food48x48_vr.dmi' + icon_state = "monkfish_raw" + nutriment_amt = 30 + w_class = ITEMSIZE_HUGE //Is that a monkfish in your pocket, or are you just happy to see me? + slice_path = /obj/item/weapon/reagent_containers/food/snacks/monkfishfillet + slices_num = 6 + trash = /obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfishremains + +/obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfish/Initialize() + ..() + bitesize = 2 + +/obj/item/weapon/reagent_containers/food/snacks/monkfishfillet + name = "monkfish fillet" + desc = "it's a fillet sliced from a monkfish." + icon = 'icons/obj/food_vr.dmi' + icon_state = "monkfish_fillet" + nutriment_amt = 5 + +/obj/item/weapon/reagent_containers/food/snacks/monkfishfillet/Initialize() + ..() + bitesize = 3 + reagents.add_reagent("protein", 1) + +/obj/item/weapon/reagent_containers/food/snacks/monkfishcooked + name = "seasoned monkfish" + desc = "a delicious slice of monkfish prepared with sweet chili and spring onion." + icon = 'icons/obj/food_vr.dmi' + icon_state = "monkfish_cooked" + nutriment_amt = 10 + nutriment_desc = list("fish" = 3, "oil" = 1, "sweet chili" = 3, "spring onion" = 2) + trash = /obj/item/trash/fancyplate + +/obj/item/weapon/reagent_containers/food/snacks/monkfishcooked/Initialize() + ..() + bitesize = 4 + reagents.add_reagent("protein", 5) + +/obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfishremains + name = "monkfish remains" + icon = 'icons/obj/food_vr.dmi' + icon_state = "monkfish_remains" + desc = "the work of a madman." + w_class = ITEMSIZE_LARGE + nutriment_amt = 10 + slice_path = /obj/item/clothing/head/fish + slices_num = 1 + +/obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfishremains/Initialize() + ..() + bitesize = 0.01 //impossible to eat + reagents.add_reagent("carbon", 5) + /obj/item/weapon/reagent_containers/food/snacks/monkeycube/sobakacube name = "sobaka cube" monkey_type = "Sobaka" @@ -490,28 +597,13 @@ filled = TRUE /obj/item/pizzabox/margherita/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/pizza/margfrozen(src) + pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margcargo(src) /obj/item/pizzabox/vegetable/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/pizza/vegfrozen(src) + pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegcargo(src) /obj/item/pizzabox/mushroom/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/pizza/mushfrozen(src) + pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushcargo(src) /obj/item/pizzabox/meat/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/pizza/meatfrozen(src) - -/obj/item/weapon/reagent_containers/food/snacks/liquidprotein - name = "\improper LiquidProtein Ration" - desc = "A variant of the liquidfood ration, designed for obligate carnivore species. Only barely more appealing than regular liquidfood. Should this be crunchy?" - icon = 'icons/obj/food_vr.dmi' - icon_state = "liquidprotein" - trash = /obj/item/trash/liquidprotein - filling_color = "#A8A8A8" - center_of_mass = list("x"=16, "y"=15) - -/obj/item/weapon/reagent_containers/food/snacks/liquidprotein/Initialize() - ..() - reagents.add_reagent("protein", 20) - reagents.add_reagent("iron", 3) - bitesize = 4 + pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatcargo(src) diff --git a/code/modules/food/glass/bottle_vr.dm b/code/modules/food/glass/bottle_vr.dm index 6def7cd873..fc1595b699 100644 --- a/code/modules/food/glass/bottle_vr.dm +++ b/code/modules/food/glass/bottle_vr.dm @@ -1,3 +1,129 @@ +/obj/item/weapon/reagent_containers/glass/bottle/bicaridine + name = "bicaridine bottle" + desc = "A small bottle. Bicaridine is an analgesic medication and can be used to treat blunt trauma." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("bicaridine" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/keloderm + name = "keloderm bottle" + desc = "A small bottle. A fifty-fifty mix of the popular burn medications kelotane and deramline." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("dermaline" = 30, "kelotane" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/dermaline + name = "dermaline bottle" + desc = "A small bottle. Dermaline is the next step in burn medication. Works twice as good as kelotane and enables the body to restore even the direst heat-damaged tissue." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("dermaline" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/carthatoline + name = "carthatoline bottle" + desc = "A small bottle. Carthatoline is strong evacuant used to treat severe poisoning." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("carthatoline" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/dexalinp + name = "dexalinp bottle" + desc = "A small bottle. Dexalin Plus is used in the treatment of oxygen deprivation. It is highly effective." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("dexalinp" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/tramadol + name = "tramadol bottle" + desc = "A small bottle. A simple, yet effective painkiller." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("tramadol" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/oxycodone + name = "oxycodone bottle" + desc = "A small bottle. An effective and very addictive painkiller." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("oxycodone" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/alkysine + name = "alkysine bottle" + desc = "A small bottle. Alkysine is a drug used to lessen the damage to neurological tissue after a catastrophic injury. Can heal brain tissue." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("alkysine" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/imidazoline + name = "imidazoline bottle" + desc = "A small bottle. Heals eye damage." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("imidazoline" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/peridaxon + name = "peridaxon bottle" + desc = "A small bottle. Used to encourage recovery of internal organs and nervous systems. Medicate cautiously." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("peridaxon" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/osteodaxon + name = "osteodaxon bottle" + desc = "A small bottle. An experimental drug used to heal bone fractures." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("osteodaxon" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/myelamine + name = "myelamine bottle" + desc = "A small bottle. Used to rapidly clot internal hemorrhages by increasing the effectiveness of platelets." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("myelamine" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/hyronalin + name = "hyronalin bottle" + desc = "A small bottle. Hyronalin is a medicinal drug used to counter the effect of radiation poisoning." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("hyronalin" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/arithrazine + name = "arithrazine bottle" + desc = "A small bottle. Arithrazine is an unstable medication used for the most extreme cases of radiation poisoning." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("arithrazine" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/spaceacillin + name = "spaceacillin bottle" + desc = "A small bottle. An all-purpose antiviral agent." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("spaceacillin" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/corophizine + name = "corophizine bottle" + desc = "A small bottle. A wide-spectrum antibiotic drug. Powerful and uncomfortable in equal doses." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("corophizine" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/rezadone + name = "rezadone bottle" + desc = "A small bottle. A powder with almost magical properties, this substance can effectively treat genetic damage in humanoids, though excessive consumption has side effects." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("rezadone" = 60) + +/obj/item/weapon/reagent_containers/glass/bottle/healing_nanites + name = "healing nanites bottle" + desc = "A small bottle. Miniature medical robots that swiftly restore bodily damage." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-4" + prefill = list("healing_nanites" = 60) + /obj/item/weapon/reagent_containers/glass/bottle/ickypak name = "ickypak bottle" desc = "A small bottle of ickypak. The smell alone makes you gag." @@ -11,3 +137,12 @@ icon = 'icons/obj/chemical.dmi' icon_state = "bottle-3" prefill = list("unsorbitol" = 60) + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/glucose + name = "glucose container" + desc = "A container of glucose. Used to treat bloodloss through a hardsuit in unconscious patients." + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/glucose/Initialize() + . = ..() + reagents.add_reagent("glucose", 100) + on_reagent_change() diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm index 0eb0683ba8..bb40355d6c 100644 --- a/code/modules/food/kitchen/microwave.dm +++ b/code/modules/food/kitchen/microwave.dm @@ -246,7 +246,7 @@ return start() if (reagents.total_volume==0 && !(locate(/obj) in ((contents - component_parts) - circuit))) //dry run - if (!wzhzhzh(10)) + if (!wzhzhzh(5)) //VOREStation Edit - Quicker Microwaves abort() return abort() @@ -257,17 +257,17 @@ if (!recipe) dirty += 1 if (prob(max(10,dirty*5))) - if (!wzhzhzh(4)) + if (!wzhzhzh(2)) //VOREStation Edit - Quicker Microwaves abort() return muck_start() - wzhzhzh(4) + wzhzhzh(2) //VOREStation Edit - Quicker Microwaves muck_finish() cooked = fail() cooked.loc = src.loc return else if (has_extra_item()) - if (!wzhzhzh(4)) + if (!wzhzhzh(2)) //VOREStation Edit - Quicker Microwaves abort() return broke() @@ -275,7 +275,7 @@ cooked.loc = src.loc return else - if (!wzhzhzh(10)) + if (!wzhzhzh(5)) //VOREStation Edit - Quicker Microwaves abort() return abort() @@ -283,7 +283,7 @@ cooked.loc = src.loc return else - var/halftime = round(recipe.time/10/2) + var/halftime = round(recipe.time/20/2) //VOREStation Edit - Quicker Microwaves if (!wzhzhzh(halftime)) abort() return @@ -303,7 +303,7 @@ if (stat & (NOPOWER|BROKEN)) return 0 use_power(500) - sleep(10) + sleep(5) //VOREStation Edit - Quicker Microwaves return 1 /obj/machinery/microwave/proc/has_extra_item() diff --git a/code/modules/food/recipes_microwave.dm b/code/modules/food/recipes_microwave.dm index f80fecdd9f..55ecc2f637 100644 --- a/code/modules/food/recipes_microwave.dm +++ b/code/modules/food/recipes_microwave.dm @@ -33,6 +33,15 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/boiledegg +/datum/recipe/devilledegg + fruit = list("chili" = 1) + reagents = list("sodiumchloride" = 2, "mayo" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/egg, + /obj/item/weapon/reagent_containers/food/snacks/egg + ) + result = /obj/item/weapon/reagent_containers/food/snacks/devilledegg + /datum/recipe/dionaroast fruit = list("apple" = 1) reagents = list("pacid" = 5) //It dissolves the carapace. Still poisonous, though. @@ -81,13 +90,6 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger -/datum/recipe/syntiburger - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger - /datum/recipe/brainburger items = list( /obj/item/weapon/reagent_containers/food/snacks/bun, @@ -205,20 +207,6 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread -/datum/recipe/syntibread - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread - /datum/recipe/xenomeatbread items = list( /obj/item/weapon/reagent_containers/food/snacks/dough, @@ -344,19 +332,11 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/human/kabob -/datum/recipe/monkeykabob +/datum/recipe/kabob //Do not put before humankabob items = list( /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey, - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/syntikabob - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, ) result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob @@ -468,11 +448,6 @@ I said no! items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) result = /obj/item/weapon/reagent_containers/food/snacks/meatsteak -/datum/recipe/syntisteak - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh) - result = /obj/item/weapon/reagent_containers/food/snacks/meatsteak - /datum/recipe/pizzamargherita fruit = list("tomato" = 1) items = list( @@ -484,6 +459,15 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita +/datum/recipe/pizzahawaiian + fruit = list("tomato" = 1, "pineapple" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cutlet, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple + /datum/recipe/meatpizza fruit = list("tomato" = 1) items = list( @@ -495,17 +479,6 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza -/datum/recipe/syntipizza - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza - /datum/recipe/mushroompizza fruit = list("mushroom" = 5, "tomato" = 1) items = list( @@ -665,6 +638,14 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich +/datum/recipe/peanutbutterjellysandwich + reagents = list("berryjuice" = 5, "peanutbutter" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/slice/bread + ) + result = /obj/item/weapon/reagent_containers/food/snacks/jellysandwich/peanutbutter + /datum/recipe/grilledcheese items = list( /obj/item/weapon/reagent_containers/food/snacks/slice/bread, @@ -874,6 +855,30 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/fishfingers +/datum/recipe/zestfish + fruit = list("lemon" = 1) + reagents = list("sodiumchloride" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/zestfish + +/datum/recipe/limezestfish + fruit = list("lime" = 1) + reagents = list("sodiumchloride" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/zestfish + +/datum/recipe/kudzudonburi + fruit = list("kudzu" = 1) + reagents = list("rice" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/kudzudonburi + /datum/recipe/mysterysoup reagents = list("water" = 10) items = list( @@ -925,6 +930,20 @@ I said no! fruit = list("cabbage" = 2, "tomato" = 1, "carrot" = 1, "apple" = 1) result = /obj/item/weapon/reagent_containers/food/snacks/tossedsalad +/datum/recipe/flowersalad + fruit = list("harebell" = 1, "poppy" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/roastedsunflower + ) + result = /obj/item/weapon/reagent_containers/food/snacks/flowerchildsalad + +/datum/recipe/rosesalad + fruit = list("harebell" = 1, "rose" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/roastedsunflower + ) + result = /obj/item/weapon/reagent_containers/food/snacks/rosesalad + /datum/recipe/aesirsalad fruit = list("goldapple" = 1, "ambrosiadeus" = 1) result = /obj/item/weapon/reagent_containers/food/snacks/aesirsalad @@ -1019,6 +1038,25 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/fries +/datum/recipe/roastedsunflowerseeds + reagents = list("sodiumchloride" = 1, "cornoil" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawsunflower + ) + result = /obj/item/weapon/reagent_containers/food/snacks/roastedsunflower + +/datum/recipe/roastedpeanutsunflowerseeds + reagents = list("sodiumchloride" = 1, "peanutoil" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawsunflower + ) + result = /obj/item/weapon/reagent_containers/food/snacks/roastedsunflower + +/datum/recipe/roastedpeanuts + fruit = list("peanut" = 2) + reagents = list("sodiumchloride" = 2, "cornoil" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/roastedpeanuts + /datum/recipe/mint reagents = list("sugar" = 5, "frostoil" = 5) result = /obj/item/weapon/reagent_containers/food/snacks/mint @@ -1156,6 +1194,11 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/cubannachos +/datum/recipe/curryrice + fruit = list("chili" = 1) + reagents = list("rice" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/curryrice + /datum/recipe/piginblanket items = list( /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, @@ -1163,10 +1206,9 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/piginblanket - // Cakes. /datum/recipe/cake - reagents = list("milk" = 5, "flour" = 15, "sugar" = 15, "egg" = 9) + reagents = list("milk" = 5, "flour" = 15, "sugar" = 15, "egg" = 9, "vanilla" = 1) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/plaincake /datum/recipe/cake/carrot @@ -1175,12 +1217,18 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/carrotcake /datum/recipe/cake/cheese + reagents = list("milk" = 5, "flour" = 15, "sugar" = 15, "egg" = 9) items = list( /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, /obj/item/weapon/reagent_containers/food/snacks/cheesewedge ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake +/datum/recipe/cake/peanut + fruit = list("peanut" = 3) + reagents = list("milk" = 5, "flour" = 10, "sugar" = 5, "egg" = 6, "peanutbutter" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake + /datum/recipe/cake/orange fruit = list("orange" = 1) reagents = list("milk" = 5, "flour" = 15, "egg" = 9,"sugar" = 5) @@ -1202,6 +1250,7 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/chocolatecake /datum/recipe/cake/birthday + reagents = list("milk" = 5, "flour" = 15, "sugar" = 15, "egg" = 9) items = list(/obj/item/clothing/head/cakehat) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/birthdaycake @@ -1211,5 +1260,6 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/applecake /datum/recipe/cake/brain + reagents = list("milk" = 5, "flour" = 15, "sugar" = 15, "egg" = 9) items = list(/obj/item/organ/internal/brain) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/braincake \ No newline at end of file + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/braincake diff --git a/code/modules/food/recipes_microwave_vr.dm b/code/modules/food/recipes_microwave_vr.dm index f9aadda69d..3cc33a96be 100644 --- a/code/modules/food/recipes_microwave_vr.dm +++ b/code/modules/food/recipes_microwave_vr.dm @@ -29,9 +29,9 @@ fruit = list("cabbage" = 1) reagents = list("rice" = 20) items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/sushi @@ -153,6 +153,26 @@ ) result = /obj/item/weapon/reagent_containers/food/snacks/makaroni +/datum/recipe/lobster + fruit = list("lemon" = 1, "cabbage" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/lobster + ) + result = /obj/item/weapon/reagent_containers/food/snacks/lobstercooked + +/datum/recipe/cuttlefish + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cuttlefish + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cuttlefishcooked + +/datum/recipe/monkfish + fruit = list("chili" = 1, "onion" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkfishfillet + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkfishcooked + /datum/recipe/margheritapizzacargo reagents = list() items = list( diff --git a/code/modules/gamemaster/actions/action.dm b/code/modules/gamemaster/actions/action.dm index aa259ccc03..f8ea16994e 100644 --- a/code/modules/gamemaster/actions/action.dm +++ b/code/modules/gamemaster/actions/action.dm @@ -7,6 +7,7 @@ var/observers_used = FALSE // Determines if the GM should check if ghosts are available before using this. var/length = 0 // Determines how long the event lasts, until end() is called. var/datum/game_master/gm = null + var/severity = 1 // The severity of the action. This is here to prevent continued future defining of this var on actions, un-used. /datum/gm_action/proc/set_up() return diff --git a/code/modules/gamemaster/actions/atmos_leak.dm b/code/modules/gamemaster/actions/atmos_leak.dm new file mode 100644 index 0000000000..34772bf1a8 --- /dev/null +++ b/code/modules/gamemaster/actions/atmos_leak.dm @@ -0,0 +1,77 @@ +/datum/gm_action/atmos_leak + name = "atmospherics leak" + departments = list(ROLE_ENGINEERING, ROLE_SYNTHETIC) + var/area/target_area // Chosen target area + var/area/target_turf // Chosen target turf in target_area + var/gas_type // Chosen gas to release + // Exclude these types and sub-types from targeting eligibilty + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + + severity + +// Decide which area will be targeted! +/datum/gm_action/atmos_leak/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 8, + EVENT_LEVEL_MODERATE = 5, + EVENT_LEVEL_MAJOR = 3 + ) + + var/gas_choices = list("carbon_dioxide", "sleeping_agent") // Annoying + if(severity >= EVENT_LEVEL_MODERATE) + gas_choices += "phoron" // Dangerous + if(severity >= EVENT_LEVEL_MAJOR) + gas_choices += "volatile_fuel" // Dangerous and no default atmos setup! + gas_type = pick(gas_choices) + + var/list/area/grand_list_of_areas = get_station_areas(excluded) + + // Okay, now lets try and pick a target! Lets try 10 times, otherwise give up + for(var/i in 1 to 10) + var/area/A = pick(grand_list_of_areas) + if(is_area_occupied(A)) + log_debug("atmos_leak event: Rejected [A] because it is occupied.") + continue + // A good area, great! Lets try and pick a turf + var/list/turfs = list() + for(var/turf/simulated/floor/F in A) + if(turf_clear(F)) + turfs += F + if(turfs.len == 0) + log_debug("atmos_leak event: Rejected [A] because it has no clear turfs.") + continue + target_area = A + target_turf = pick(turfs) + + // If we can't find a good target, give up + if(!target_area) + log_debug("atmos_leak event: Giving up after too many failures to pick target area") + return + +/datum/gm_action/atmos_leak/announce() + if(target_area) + command_announcement.Announce("Warning, hazardous [gas_data.name[gas_type]] gas leak detected in \the [target_area], evacuate the area.", "Hazard Alert") + +/datum/gm_action/atmos_leak/start() + if(!target_turf) + return + ..() + spawn(rand(0, 600)) + // Okay, time to actually put the gas in the room! + // TODO - Would be nice to break a waste pipe perhaps? + // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around + + // Fow now just add a bunch of it to the air + var/datum/gas_mixture/air_contents = new + air_contents.temperature = T20C + ((severity - 1) * rand(-50, 50)) + air_contents.gas[gas_type] = 10 * MOLES_CELLSTANDARD + target_turf.assume_air(air_contents) + playsound(target_turf, 'sound/effects/smoke.ogg', 50, 1) + +/datum/gm_action/atmos_leak/get_weight() + return 15 + (metric.count_people_in_department(ROLE_ENGINEERING) * 10 + metric.count_people_in_department(ROLE_SYNTHETIC) * 30) // Synthetics are counted in higher value because they can wirelessly connect to alarms. diff --git a/code/modules/gamemaster/actions/blob.dm b/code/modules/gamemaster/actions/blob.dm new file mode 100644 index 0000000000..31a4518a69 --- /dev/null +++ b/code/modules/gamemaster/actions/blob.dm @@ -0,0 +1,74 @@ +/datum/gm_action/blob + name = "blob infestation" + departments = list(ROLE_ENGINEERING, ROLE_SECURITY, ROLE_MEDICAL) + chaotic = 25 + + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + + var/area/target_area // Chosen target area + var/turf/target_turf // Chosen target turf in target_area + + var/obj/structure/blob/core/Blob + var/spawn_blob_type = /obj/structure/blob/core/random_medium + +/datum/gm_action/blob/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 4, + EVENT_LEVEL_MODERATE = 2, + EVENT_LEVEL_MAJOR = 1 + ) + + var/list/area/grand_list_of_areas = get_station_areas(excluded) + + for(var/i in 1 to 10) + var/area/A = pick(grand_list_of_areas) + if(is_area_occupied(A)) + log_debug("Blob infestation event: Rejected [A] because it is occupied.") + continue + var/list/turfs = list() + for(var/turf/simulated/floor/F in A) + if(turf_clear(F)) + turfs += F + if(turfs.len == 0) + log_debug("Blob infestation event: Rejected [A] because it has no clear turfs.") + continue + target_area = A + target_turf = pick(turfs) + + if(!target_area) + log_debug("Blob infestation event: Giving up after too many failures to pick target area") + +/datum/gm_action/blob/start() + ..() + var/turf/T + + if(severity == EVENT_LEVEL_MUNDANE || !target_area || !target_turf) + T = pick(blobstart) + else if(severity == EVENT_LEVEL_MODERATE) + T = target_turf + else + T = target_turf + spawn_blob_type = /obj/structure/blob/core/random_hard + + Blob = new spawn_blob_type(T) + +/datum/gm_action/blob/announce() + spawn(rand(600, 3000)) // 1-5 minute leeway for the blob to go un-detected. + command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') + +/datum/gm_action/blob/get_weight() + var/engineers = metric.count_people_in_department(ROLE_ENGINEERING) + var/security = metric.count_people_in_department(ROLE_SECURITY) + var/medical = metric.count_people_in_department(ROLE_MEDICAL) + + var/assigned_staff = engineers + security + if(engineers || security) // Medical only counts if one of the other two exists, and even then they count as half. + assigned_staff += round(medical / 2) + + var/weight = (max(assigned_staff - 2, 0) * 20) // An assigned staff count of 2 must be had to spawn a blob. + return weight diff --git a/code/modules/gamemaster/actions/brand_intelligence.dm b/code/modules/gamemaster/actions/brand_intelligence.dm new file mode 100644 index 0000000000..156e74f3fa --- /dev/null +++ b/code/modules/gamemaster/actions/brand_intelligence.dm @@ -0,0 +1,69 @@ +/datum/gm_action/brand_intelligence + name = "rampant vending machines" + length = 30 MINUTES + departments = list(ROLE_ENGINEERING, ROLE_EVERYONE) + + var/list/obj/machinery/vending/vendingMachines = list() + var/list/obj/machinery/vending/infectedVendingMachines = list() + var/obj/machinery/vending/originMachine + var/start_time = 0 + + var/active = FALSE // Are we currently infecting? + +/datum/gm_action/brand_intelligence/announce() + if(prob(90)) + command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard [station_name()], which appears to transmit \ + to other nearby vendors. The original infected machine is believed to be \a [originMachine.name].", "Vendor Service Alert") + +/datum/gm_action/brand_intelligence/set_up() + vendingMachines.Cut() + infectedVendingMachines.Cut() + + for(var/obj/machinery/vending/V in machines) + if(isNotStationLevel(V.z)) continue + vendingMachines.Add(V) + + if(!vendingMachines.len) + length = 0 + return + + originMachine = pick(vendingMachines) + vendingMachines.Remove(originMachine) + originMachine.shut_up = 0 + originMachine.shoot_inventory = 1 + + start_time = world.time + active = TRUE + +/datum/gm_action/brand_intelligence/start() + ..() + while(originMachine || active) + if(!vendingMachines.len || !originMachine || originMachine.shut_up) //if every machine is infected, or if the original vending machine is missing or has it's voice switch flipped + end() + return + + if(ISMULTIPLE(world.time - start_time, 5)) + if(prob(15)) + var/obj/machinery/vending/infectedMachine = pick(vendingMachines) + vendingMachines.Remove(infectedMachine) + infectedVendingMachines.Add(infectedMachine) + infectedMachine.shut_up = 0 + infectedMachine.shoot_inventory = 1 + + if(ISMULTIPLE(world.time - start_time, 12)) + originMachine.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obsession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) + +/datum/gm_action/brand_intelligence/end() + active = FALSE + for(var/obj/machinery/vending/infectedMachine in infectedVendingMachines) + infectedMachine.shut_up = 1 + infectedMachine.shoot_inventory = 0 + +/datum/gm_action/brand_intelligence/get_weight() + return 60 + (metric.count_people_in_department(ROLE_ENGINEERING) * 20) diff --git a/code/modules/gamemaster/actions/camera_damage.dm b/code/modules/gamemaster/actions/camera_damage.dm new file mode 100644 index 0000000000..583e57096b --- /dev/null +++ b/code/modules/gamemaster/actions/camera_damage.dm @@ -0,0 +1,52 @@ +/datum/gm_action/camera_damage + name = "random camera damage" + reusable = TRUE + departments = list(ROLE_SYNTHETIC, ROLE_ENGINEERING) + +/datum/gm_action/camera_damage/start() + var/obj/machinery/camera/C = acquire_random_camera() + if(!C) + return + ..() + + var/severity_range = 0 + severity = pickweight(EVENT_LEVEL_MUNDANE = 10, + EVENT_LEVEL_MODERATE = 5, + EVENT_LEVEL_MAJOR = 1 + ) + + switch(severity) + if(EVENT_LEVEL_MUNDANE) + severity_range = 0 + if(EVENT_LEVEL_MODERATE) + severity_range = 7 + if(EVENT_LEVEL_MAJOR) + severity_range = 15 + + for(var/obj/machinery/camera/cam in range(severity_range,C)) + if(is_valid_camera(cam)) + if(prob(2*severity)) + cam.destroy() + else + cam.wires.UpdateCut(CAMERA_WIRE_POWER, 0) + if(prob(5*severity)) + cam.wires.UpdateCut(CAMERA_WIRE_ALARM, 0) + +/datum/gm_action/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) + if(!cameranet.cameras.len) + return + if(!remaining_attempts) + return + + var/obj/machinery/camera/C = pick(cameranet.cameras) + if(is_valid_camera(C)) + return C + return acquire_random_camera(remaining_attempts--) + +/datum/gm_action/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access + var/turf/T = get_turf(C) + return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) + +/datum/gm_action/camera_damage/get_weight() + return 40 + (metric.count_people_in_department(ROLE_ENGINEERING) * 20) + (metric.count_people_in_department(ROLE_SYNTHETIC) * 40) diff --git a/code/modules/gamemaster/actions/carp_migration.dm b/code/modules/gamemaster/actions/carp_migration.dm index b635ca6d5e..eabb2ce79a 100644 --- a/code/modules/gamemaster/actions/carp_migration.dm +++ b/code/modules/gamemaster/actions/carp_migration.dm @@ -8,14 +8,7 @@ length = 20 MINUTES /datum/gm_action/carp_migration/get_weight() - var/people_in_space = 0 - for(var/mob/living/L in player_list) - if(!(L.z in using_map.station_levels)) - continue // Not on the right z-level. - var/turf/T = get_turf(L) - if(istype(T, /turf/space) && istype(T.loc,/area/space)) - people_in_space++ - return 50 + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (people_in_space * 20) + return 50 + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (metric.count_all_space_mobs() * 20) /datum/gm_action/carp_migration/announce() var/announcement = "Unknown biological entities have been detected near [station_name()], please stand-by." diff --git a/code/modules/gamemaster/actions/electrical_storm.dm b/code/modules/gamemaster/actions/electrical_storm.dm new file mode 100644 index 0000000000..008a345a44 --- /dev/null +++ b/code/modules/gamemaster/actions/electrical_storm.dm @@ -0,0 +1,33 @@ +/datum/gm_action/electrical_storm + name = "electrical storm" + departments = list(ROLE_EVERYONE) + reusable = TRUE + var/lightsoutAmount = 1 + var/lightsoutRange = 25 + +/datum/gm_action/electrical_storm/announce() + command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert") + +/datum/gm_action/electrical_storm/start() + ..() + var/list/epicentreList = list() + + for(var/i=1, i <= lightsoutAmount, i++) + var/list/possibleEpicentres = list() + for(var/obj/effect/landmark/newEpicentre in landmarks_list) + if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList)) + possibleEpicentres += newEpicentre + if(possibleEpicentres.len) + epicentreList += pick(possibleEpicentres) + else + break + + if(!epicentreList.len) + return + + for(var/obj/effect/landmark/epicentre in epicentreList) + for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange)) + apc.overload_lighting() + +/datum/gm_action/electrical_storm/get_weight() + return 30 + (metric.count_people_in_department(ROLE_ENGINEERING) * 15) + (metric.count_people_in_department(ROLE_EVERYONE) * 5) diff --git a/code/modules/gamemaster/actions/electrified_door.dm b/code/modules/gamemaster/actions/electrified_door.dm new file mode 100644 index 0000000000..e11c72f345 --- /dev/null +++ b/code/modules/gamemaster/actions/electrified_door.dm @@ -0,0 +1,75 @@ +/datum/gm_action/electrified_door + name = "airlock short-circuit" + departments = list(ROLE_ENGINEERING, ROLE_MEDICAL) + chaotic = 10 + var/obj/machinery/door/airlock/chosen_door + var/area/target_area + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters + ) + +/datum/gm_action/electrified_door/set_up() + var/list/area/grand_list_of_areas = get_station_areas(excluded) + + severity = pickweight(EVENT_LEVEL_MUNDANE = 10, + EVENT_LEVEL_MODERATE = 5, + EVENT_LEVEL_MAJOR = 1 + ) + + //try 10 times + for(var/i in 1 to 10) + target_area = pick(grand_list_of_areas) + var/list/obj/machinery/door/airlock/target_doors = list() + for(var/obj/machinery/door/airlock/target_door in target_area.contents) + target_doors += target_door + target_doors = shuffle(target_doors) + + for(var/obj/machinery/door/airlock/target_door in target_doors) + if(!target_door.isElectrified() && target_door.arePowerSystemsOn() && target_door.maxhealth == target_door.health) + chosen_door = target_door + return + +/datum/gm_action/electrified_door/start() + ..() + if(!chosen_door) + return + command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert") + chosen_door.visible_message("\The [chosen_door]'s panel sparks!") + chosen_door.set_safeties(0) + playsound(get_turf(chosen_door), 'sound/machines/buzz-sigh.ogg', 50, 1) + if(severity >= EVENT_LEVEL_MODERATE) + chosen_door.electrify(-1) + spawn(rand(10 SECONDS, 2 MINUTES)) + if(chosen_door && chosen_door.arePowerSystemsOn() && prob(25 + 25 * severity)) + command_announcement.Announce("Overload has been localized to \the [target_area].", "Electrical Alert") + + if(severity >= EVENT_LEVEL_MAJOR) // New Major effect. Hydraulic boom. + spawn() + chosen_door.visible_message("\The [chosen_door] buzzes.") + playsound(get_turf(chosen_door), 'sound/machines/buzz-sigh.ogg', 50, 1) + sleep(rand(10 SECONDS, 3 MINUTES)) + if(!chosen_door || !chosen_door.arePowerSystemsOn()) + return + chosen_door.visible_message("\The [chosen_door]'s hydraulics creak.") + playsound(get_turf(chosen_door), 'sound/machines/airlock_creaking.ogg', 50, 1) + sleep(rand(30 SECONDS, 10 MINUTES)) + if(!chosen_door || !chosen_door.arePowerSystemsOn()) + return + chosen_door.visible_message("\The [chosen_door] emits a hydraulic shriek!") + playsound(get_turf(chosen_door), 'sound/machines/airlock.ogg', 80, 1) + spawn(rand(5 SECONDS, 30 SECONDS)) + if(!chosen_door || !chosen_door.arePowerSystemsOn()) + return + chosen_door.visible_message("\The [chosen_door]'s hydraulics detonate!") + chosen_door.fragmentate(get_turf(chosen_door), rand(5, 10), rand(3, 5), list(/obj/item/projectile/bullet/pellet/fragment/tank/small)) + explosion(get_turf(chosen_door),-1,-1,2,3) + + chosen_door.lock() + chosen_door.health = chosen_door.maxhealth / 6 + chosen_door.aiControlDisabled = 1 + chosen_door.update_icon() + +/datum/gm_action/electrified_door/get_weight() + return 10 + (metric.count_people_in_department(ROLE_ENGINEERING) * 5 + metric.count_people_in_department(ROLE_MEDICAL) * 10) diff --git a/code/modules/gamemaster/actions/gravity.dm b/code/modules/gamemaster/actions/gravity.dm new file mode 100644 index 0000000000..e895ca2cfa --- /dev/null +++ b/code/modules/gamemaster/actions/gravity.dm @@ -0,0 +1,36 @@ +/datum/gm_action/gravity + name = "gravity failure" + departments = list(ROLE_EVERYONE) + length = 600 + var/list/zLevels + +/datum/gm_action/gravity/set_up() + length = rand(length, length * 5) + // Setup which levels we will disrupt gravit on. + zLevels = using_map.station_levels.Copy() + for(var/datum/planet/P in SSplanets.planets) + zLevels -= P.expected_z_levels + +/datum/gm_action/gravity/announce() + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system \ + reinitializes. Please stand by while the gravity system reinitializes.", "Gravity Failure") + +/datum/gm_action/gravity/start() + ..() + gravity_is_on = 0 + for(var/area/A in all_areas) + if(A.z in zLevels) + A.gravitychange(gravity_is_on, A) + +/datum/gm_action/gravity/end() + if(!gravity_is_on) + gravity_is_on = 1 + + for(var/area/A in all_areas) + if(A.z in zLevels) + A.gravitychange(gravity_is_on, A) + + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") + +/datum/gm_action/gravity/get_weight() + return 30 + (metric.count_people_in_department(ROLE_EVERYONE) * 20) diff --git a/code/modules/gamemaster/actions/ion_storm.dm b/code/modules/gamemaster/actions/ion_storm.dm new file mode 100644 index 0000000000..e733bd4bd1 --- /dev/null +++ b/code/modules/gamemaster/actions/ion_storm.dm @@ -0,0 +1,43 @@ +/datum/gm_action/ionstorm + var/botEmagChance = 0.5 + var/list/players = list() + var/active = FALSE + length = 1 MINUTE + +/datum/gm_action/ionstorm/set_up() + length = rand(length, length * 10) +// command_alert("The station has entered an ion storm. Monitor all electronic equipment for malfunctions", "Anomaly Alert") + for (var/mob/living/carbon/human/player in player_list) + if( !player.mind || player_is_antag(player.mind, only_offstation_roles = 1) || player.client.inactivity > MinutesToTicks(10)) + continue + players += player.real_name + + for (var/mob/living/silicon/ai/target in silicon_mob_list) + var/law = target.generate_ion_law() + target << "You have detected a change in your laws information:" + target << law + target.add_ion_law(law) + target.show_laws() + +/datum/gm_action/ionstorm/announce() + if(message_servers) + for (var/obj/machinery/message_server/MS in message_servers) + MS.spamfilter.Cut() + var/i + for (i = 1, i <= MS.spamfilter_limit, i++) + MS.spamfilter += pick("kitty","HONK","rev","malf","liberty","freedom","drugs", "[using_map.station_short]", \ + "admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\ + "director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno") + +/datum/gm_action/ionstorm/start() + while(active) + sleep(1) + if(botEmagChance) + for(var/mob/living/bot/bot in mob_list) + if(prob(botEmagChance)) + bot.emag_act(1) + +/datum/gm_action/ionstorm/end() + spawn(rand(5000,8000)) + if(prob(50)) + ion_storm_announcement() diff --git a/code/modules/gamemaster/actions/meteor_defense.dm b/code/modules/gamemaster/actions/meteor_defense.dm index 1bae723b00..5df197f160 100644 --- a/code/modules/gamemaster/actions/meteor_defense.dm +++ b/code/modules/gamemaster/actions/meteor_defense.dm @@ -34,10 +34,10 @@ /datum/gm_action/meteor_defense/start() ..() spawn(0) - // sleep(5 MINUTES) + sleep(5 MINUTES) var/announcement = "The incoming debris are expected to approach from the [dir_text] side. ETA to arrival is approximately 5 minutes." command_announcement.Announce(announcement, "Meteor Alert - Update") - // sleep(5 MINUTES) + sleep(5 MINUTES) announcement = "Incoming debris approaches from the [dir_text] side!" command_announcement.Announce(announcement, "Meteor Alert - Update") while(waves) diff --git a/code/modules/gamemaster/actions/money_hacker.dm b/code/modules/gamemaster/actions/money_hacker.dm new file mode 100644 index 0000000000..69c3e906c7 --- /dev/null +++ b/code/modules/gamemaster/actions/money_hacker.dm @@ -0,0 +1,79 @@ +/var/global/account_hack_attempted = 0 + +/datum/gm_action/money_hacker + name = "bank account hacker" + departments = list(ROLE_EVERYONE) + reusable = TRUE + var/datum/money_account/affected_account + var/active + var/activeFor + var/end_time + +/datum/gm_action/money_hacker/set_up() + active = TRUE + end_time = world.time + 6000 + if(all_money_accounts.len) + affected_account = pick(all_money_accounts) + + account_hack_attempted = 1 + +/datum/gm_action/money_hacker/announce() + var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[affected_account.account_number], \ + without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ + Notifications will be sent as updates occur.
      " + var/my_department = "[station_name()] firewall subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) continue + MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) + + +/datum/gm_action/money_hacker/start() + ..() + spawn(0) + while(active) + sleep(1) + activeFor++ + if(world.time >= end_time) + length = activeFor + else + length = activeFor + 10 + +/datum/gm_action/money_hacker/end() + active = FALSE + var/message + if(affected_account && !affected_account.suspended) + //hacker wins + message = "The hack attempt has succeeded." + + //subtract the money + var/lost = affected_account.money * 0.8 + (rand(2,4) - 2) / 10 + affected_account.money -= lost + + //create a taunting log entry + var/datum/transaction/T = new() + T.target_name = pick("","yo brotha from anotha motha","el Presidente","chieF smackDowN","Nobody") + T.purpose = pick("Ne$ ---ount fu%ds init*&lisat@*n","PAY BACK YOUR MUM","Funds withdrawal","pWnAgE","l33t hax","liberationez","Hit","Nothing") + T.amount = pick("","([rand(0,99999)])","alla money","9001$","HOLLA HOLLA GET DOLLA","([lost])") + var/date1 = "31 December, 1999" + var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" + T.date = pick("", current_date_string, date1, date2,"Nowhen") + var/time1 = rand(0, 99999999) + var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" + T.time = pick("", stationtime2text(), time2, "Never") + T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") + + affected_account.transaction_log.Add(T) + + else + //crew wins + message = "The attack has ceased, the affected accounts can now be brought online." + + var/my_department = "[station_name()] firewall subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) continue + MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) + +/datum/gm_action/money_hacker/get_weight() + return 30 * all_money_accounts.len diff --git a/code/modules/gamemaster/actions/money_lotto.dm b/code/modules/gamemaster/actions/money_lotto.dm new file mode 100644 index 0000000000..1cb7f92e1e --- /dev/null +++ b/code/modules/gamemaster/actions/money_lotto.dm @@ -0,0 +1,39 @@ +/datum/gm_action/money_lotto + name = "lottery win" + departments = list(ROLE_EVERYONE) + var/winner_name = "John Smith" + var/winner_sum = 0 + var/deposit_success = 0 + +/datum/gm_action/money_lotto/start() + ..() + winner_sum = pick(5000, 10000, 50000, 100000, 500000, 1000000, 1500000) + if(all_money_accounts.len) + var/datum/money_account/D = pick(all_money_accounts) + winner_name = D.owner_name + if(!D.suspended) + D.money += winner_sum + + var/datum/transaction/T = new() + T.target_name = "The [using_map.starsys_name] Times Grand Slam -Stellar- Lottery" + T.purpose = "Winner!" + T.amount = winner_sum + T.date = current_date_string + T.time = stationtime2text() + T.source_terminal = "Sif TCD Terminal #[rand(111,333)]" + D.transaction_log.Add(T) + + deposit_success = 1 + +/datum/gm_action/money_lotto/announce() + var/author = "[using_map.company_name] Editor" + var/channel = "The [using_map.starsys_name] Times" + + var/body = "The [using_map.starsys_name] Times wishes to congratulate [winner_name] for recieving the [using_map.starsys_name] Stellar Slam Lottery, and receiving the out of this world sum of [winner_sum] credits!" + if(!deposit_success) + body += "
      Unfortunately, we were unable to verify the account details provided, so we were unable to transfer the money. Send a cheque containing the sum of 5000 Thalers to ND 'Stellar Slam' office on the The [using_map.starsys_name] Times gateway containing updated details, and your winnings'll be re-sent within the month." + + news_network.SubmitArticle(body, author, channel, null, 1) + +/datum/gm_action/money_lotto/get_weight() + return 25 * metric.count_people_in_department(ROLE_EVERYONE) diff --git a/code/modules/gamemaster/actions/money_spam.dm b/code/modules/gamemaster/actions/money_spam.dm new file mode 100644 index 0000000000..e11baec090 --- /dev/null +++ b/code/modules/gamemaster/actions/money_spam.dm @@ -0,0 +1,131 @@ +/datum/gm_action/pda_spam + name = "PDA spam" + departments = list(ROLE_EVERYONE) + reusable = TRUE + var/last_spam_time = 0 + var/obj/machinery/message_server/useMS + var/obj/machinery/exonet_node/node + +/datum/gm_action/pda_spam/set_up() + last_spam_time = world.time + pick_message_server() + +/datum/gm_action/pda_spam/proc/pick_message_server() + if(message_servers) + for (var/obj/machinery/message_server/MS in message_servers) + if(MS.active) + useMS = MS + break + +/datum/gm_action/pda_spam/start() + ..() + while(world.time < last_spam_time + 3000) + if(!node) + node = get_exonet_node() + + if(!node || !node.on || !node.allow_external_PDAs) + return + + if(!useMS || !useMS.active) + useMS = null + pick_message_server() + + if(useMS) + if(prob(5)) + // /obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") + var/obj/item/device/pda/P + var/list/viables = list() + for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) + if (!check_pda.owner||check_pda.toff||check_pda == src||check_pda.hidden) + continue + viables.Add(check_pda) + + if(!viables.len) + return + P = pick(viables) + + var/sender + var/message + switch(pick(1,2,3,4,5,6,7)) + if(1) + sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") + message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ + "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ + "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ + "You will be able to enjoy over 450 top-flight casino games at MaxBet.") + if(2) + sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") + message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ + "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ + "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ + "You have (1) new message!",\ + "You have (2) new profile views!") + if(3) + sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") + message = pick("Luxury watches for Blowout sale prices!",\ + "Watches, Jewelry & Accessories, Bags & Wallets !",\ + "Deposit 100$ and get 300$ totally free!",\ + " 100K NT.|WOWGOLD õnly $89 ",\ + "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ + "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") + if(4) + sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") + message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ + "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ + "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ + "Men of all species report AMAZING increases in length, width and stamina.") + if(5) + sender = pick("Dr","Crown prince","King Regent","Professor","Captain") + sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") + sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") + message = pick("YOUR FUND HAS BEEN MOVED TO [pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus")] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ + "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ + "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ + "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ + "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") + if(6) + sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") + message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ + "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ + "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ + "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") + if(7) + sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") + message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ + "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ + "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ + "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") + + if (useMS.send_pda_message("[P.owner]", sender, message)) //Message been filtered by spam filter. + return + + last_spam_time = world.time + + /* //VOREStation Removal: no need to spam the AI tenfold + if (prob(50)) //Give the AI an increased chance to intercept the message + for(var/mob/living/silicon/ai/ai in mob_list) + // Allows other AIs to intercept the message but the AI won't intercept their own message. + if(ai.aiPDA != P && ai.aiPDA != src) + ai.show_message("Intercepted message from [sender] (Unknown / spam?) to [P:owner]: [message]") + */ + + //Commented out because we don't send messages like this anymore. Instead it will just popup in their chat window. + //P.tnote += "← From [sender] (Unknown / spam?):
      [message]
      " + + if (!P.message_silent) + playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(3, P.loc)) + if(!P.message_silent) O.show_message(text("\icon[P] *[P.ttone]*")) + //Search for holder of the PDA. + var/mob/living/L = null + if(P.loc && isliving(P.loc)) + L = P.loc + //Maybe they are a pAI! + else + L = get(P, /mob/living/silicon) + + if(L) + L << "\icon[P] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)" + +/datum/gm_action/pda_spam/get_weight() + return 25 * metric.count_people_in_department(ROLE_EVERYONE) diff --git a/code/modules/gamemaster/actions/planet_weather_change.dm b/code/modules/gamemaster/actions/planet_weather_change.dm new file mode 100644 index 0000000000..27583a9a6a --- /dev/null +++ b/code/modules/gamemaster/actions/planet_weather_change.dm @@ -0,0 +1,35 @@ +/datum/gm_action/planet_weather_shift + name = "sudden weather shift" + enabled = TRUE + departments = list(ROLE_EVERYONE) + reusable = TRUE + var/datum/planet/target_planet + + var/list/banned_weathers = list( + //VOREStation Edit - Virgo 3B Weather, + /datum/weather/virgo3b/ash_storm, + /datum/weather/virgo3b/emberfall, + /datum/weather/virgo3b/blood_moon, + /datum/weather/virgo3b/fallout) + //VOREStation Edit End + var/list/possible_weathers = list() + +/datum/gm_action/planet_weather_shift/set_up() + if(!target_planet || isnull(target_planet)) + target_planet = pick(SSplanets.planets) + possible_weathers |= target_planet.weather_holder.allowed_weather_types + possible_weathers -= banned_weathers + return + +/datum/gm_action/planet_weather_shift/get_weight() + return max(0, -15 + (metric.count_all_outdoor_mobs() * 20)) + +/datum/gm_action/planet_weather_shift/start() + ..() + var/new_weather = pick(possible_weathers) + target_planet.weather_holder.change_weather(new_weather) + +/datum/gm_action/planet_weather_shift/announce() + spawn(rand(3 SECONDS, 2 MINUTES)) + command_announcement.Announce("Local weather patterns on [target_planet.name] suggest that a sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of rapidly deteriorating conditions.", "Weather Alert") + return diff --git a/code/modules/gamemaster/actions/prison_break.dm b/code/modules/gamemaster/actions/prison_break.dm new file mode 100644 index 0000000000..c034c7e223 --- /dev/null +++ b/code/modules/gamemaster/actions/prison_break.dm @@ -0,0 +1,93 @@ +/datum/gm_action/prison_break + name = "prison break" + departments = list(ROLE_SECURITY, ROLE_SYNTHETIC) + + var/start_time = 0 + var/active = FALSE // Are we doing stuff? + var/releaseWhen = 60 // The delay for the breakout to occur. + var/list/area/areas = list() // List of areas to affect. Filled by start() + + var/eventDept = "Security" // Department name in announcement + var/list/areaName = list("Brig") // Names of areas mentioned in AI and Engineering announcements + var/list/areaType = list(/area/security/prison, /area/security/brig) // Area types to include. + var/list/areaNotType = list() // Area types to specifically exclude. + +/datum/gm_action/prison_break/get_weight() + var/afflicted_staff = 0 + var/assigned_staff = metric.count_people_in_department(ROLE_ENGINEERING) + for(var/department in departments) + afflicted_staff += round(metric.count_people_in_department(department) / 2) + + var/weight = 20 + (assigned_staff * 10) + + if(assigned_staff) + weight += afflicted_staff + + return weight + +/datum/gm_action/prison_break/virology + name = "virology breakout" + departments = list(ROLE_MEDICAL, ROLE_SYNTHETIC) + eventDept = "Medical" + areaName = list("Virology") + areaType = list(/area/medical/virology, /area/medical/virologyaccess) + +/datum/gm_action/prison_break/xenobiology + name = "xenobiology breakout" + departments = list(ROLE_RESEARCH, ROLE_SYNTHETIC) + eventDept = "Science" + areaName = list("Xenobiology") + areaType = list(/area/rnd/xenobiology) + areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) + +/datum/gm_action/prison_break/station + name = "station-wide breakout" + departments = list(ROLE_SECURITY, ROLE_MEDICAL, ROLE_RESEARCH, ROLE_SYNTHETIC) + eventDept = "Station" + areaName = list("Brig","Virology","Xenobiology") + areaType = list(/area/security/prison, /area/security/brig, /area/medical/virology, /area/medical/virologyaccess, /area/rnd/xenobiology) + areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) + +/datum/gm_action/prison_break/set_up() + releaseWhen = rand(60, 90) + start_time = world.time + active = TRUE + length = releaseWhen + 1 SECOND + +/datum/gm_action/prison_break/announce() + if(areas && areas.len > 0) + command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] detected in [station_name()] [(eventDept == "Security")? "imprisonment":"containment"] subroutines. Secure any compromised areas immediately. Station AI involvement is recommended.", "[eventDept] Alert") + +/datum/gm_action/prison_break/start() + ..() + for(var/area/A in all_areas) + if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) + areas += A + + if(areas && areas.len > 0) + var/my_department = "[station_name()] firewall subroutines" + var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.
      " + for(var/obj/machinery/message_server/MS in machines) + MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) + for(var/mob/living/silicon/ai/A in player_list) + A << "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department]." + + else + world.log << "ERROR: Could not initate grey-tide. Unable to find suitable containment area." + + if(areas && areas.len > 0) + spawn() + while(active) + sleep(1) + if(world.time >= releaseWhen + start_time) + var/obj/machinery/power/apc/theAPC = null + for(var/area/A in areas) + theAPC = A.get_apc() + if(theAPC.operating) //If the apc's off, it's a little hard to overload the lights. + for(var/obj/machinery/light/L in A) + L.flicker(10) + +/datum/gm_action/prison_break/end() + active = FALSE + for(var/area/A in shuffle(areas)) + A.prison_break() diff --git a/code/modules/gamemaster/actions/radiation_storm.dm b/code/modules/gamemaster/actions/radiation_storm.dm new file mode 100644 index 0000000000..678ad16ab6 --- /dev/null +++ b/code/modules/gamemaster/actions/radiation_storm.dm @@ -0,0 +1,67 @@ +/datum/gm_action/radiation_storm + name = "radiation storm" + departments = list(ROLE_EVERYONE) + reusable = TRUE + + var/enterBelt = 30 + var/radIntervall = 5 + var/leaveBelt = 80 + var/revokeAccess = 165 + var/activeFor = 0 + var/postStartTicks = 0 + var/active = FALSE + +/datum/gm_action/radiation_storm/announce() + command_announcement.Announce("High levels of radiation detected near \the [station_name()]. Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + +/datum/gm_action/radiation_storm/set_up() + active = TRUE + +/datum/gm_action/radiation_storm/start() + ..() + make_maint_all_access() + + while(active) + sleep(1 SECOND) + activeFor ++ + if(activeFor == enterBelt) + command_announcement.Announce("The station has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + radiate() + + if(activeFor >= enterBelt && activeFor <= leaveBelt) + postStartTicks++ + + if(postStartTicks == radIntervall) + postStartTicks = 0 + radiate() + + else if(activeFor == leaveBelt) + command_announcement.Announce("The station has passed the radiation belt. Please allow for up to one minute while radiation levels dissipate, and report to medbay if you experience any unusual symptoms. Maintenance will lose all access again shortly.", "Anomaly Alert") + +/datum/gm_action/radiation_storm/proc/radiate() + var/radiation_level = rand(15, 35) + for(var/z in using_map.station_levels) + SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) + + for(var/mob/living/carbon/C in living_mob_list) + var/area/A = get_area(C) + if(!A) + continue + if(A.flags & RAD_SHIELDED) + continue + if(istype(C,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + if(prob(5)) + if (prob(75)) + randmutb(H) // Applies bad mutation + domutcheck(H,null,MUTCHK_FORCED) + else + randmutg(H) // Applies good mutation + domutcheck(H,null,MUTCHK_FORCED) + +/datum/gm_action/radiation_storm/end() + spawn(revokeAccess SECONDS) + revoke_maint_all_access() + +/datum/gm_action/radiation_storm/get_weight() + return 20 + (metric.count_people_in_department(ROLE_MEDICAL) * 10) + (metric.count_all_space_mobs() * 40) + (metric.count_people_in_department(ROLE_EVERYONE) * 20) diff --git a/code/modules/gamemaster/actions/random_antagonist.dm b/code/modules/gamemaster/actions/random_antagonist.dm new file mode 100644 index 0000000000..b1cb7b01d8 --- /dev/null +++ b/code/modules/gamemaster/actions/random_antagonist.dm @@ -0,0 +1,23 @@ +// The random spawn proc on the antag datum will handle announcing the spawn and whatnot. +/datum/gm_action/random_antag + name = "random antagonist" + departments = list(ROLE_EVERYONE) + chaotic = 30 + reusable = TRUE + +/datum/gm_action/random_antag/start() + ..() + var/list/valid_types = list() + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag.flags & ANTAG_RANDSPAWN) + valid_types |= antag + if(valid_types.len) + var/datum/antagonist/antag = pick(valid_types) + antag.attempt_random_spawn() + +/datum/gm_action/random_antag/get_weight() + . = ..() + if(gm) + var/weight = max(0, (metric.count_people_in_department(ROLE_SECURITY) * 20) + (metric.count_people_in_department(ROLE_EVERYONE) * 5) + gm.staleness) + return weight diff --git a/code/modules/gamemaster/actions/rogue_drones.dm b/code/modules/gamemaster/actions/rogue_drones.dm new file mode 100644 index 0000000000..983aa87842 --- /dev/null +++ b/code/modules/gamemaster/actions/rogue_drones.dm @@ -0,0 +1,63 @@ +/datum/gm_action/rogue_drone + name = "rogue drones" + departments = list(ROLE_SECURITY) + chaotic = 60 + length = 20 MINUTES + var/list/drones_list = list() + +/datum/gm_action/rogue_drone/start() + ..() + //spawn them at the same place as carp + var/list/possible_spawns = list() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == "carpspawn") + possible_spawns.Add(C) + + //25% chance for this to be a false alarm + var/num + if(prob(25)) + num = 0 + else + num = rand(2,6) + for(var/i=0, i drones_list.len * 0.75) + command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") + else + command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") + +/datum/gm_action/rogue_drone/get_weight() + return 20 + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (metric.count_all_space_mobs() * 30) diff --git a/code/modules/gamemaster/actions/solar_storm.dm b/code/modules/gamemaster/actions/solar_storm.dm new file mode 100644 index 0000000000..046b93639b --- /dev/null +++ b/code/modules/gamemaster/actions/solar_storm.dm @@ -0,0 +1,56 @@ +/datum/gm_action/solar_storm + name = "solar storm" + var/rad_interval = 1 SECOND + var/base_solar_gen_rate + length = 3 MINUTES + var/duration // Duration for the storm + var/start_time = 0 + + reusable = TRUE + +/datum/gm_action/solar_storm/set_up() + start_time = world.time + duration = length + duration += rand(-1 * 1 MINUTE, 1 MINUTE) + +/datum/gm_action/solar_storm/announce() + command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + adjust_solar_output(1.5) + +/datum/gm_action/solar_storm/proc/adjust_solar_output(var/mult = 1) + if(isnull(base_solar_gen_rate)) base_solar_gen_rate = GLOB.solar_gen_rate + GLOB.solar_gen_rate = mult * base_solar_gen_rate + +/datum/gm_action/solar_storm/start() + ..() + length = duration + command_announcement.Announce("The solar storm has reached the station. Please refain from EVA and remain inside the station until it has passed.", "Anomaly Alert") + adjust_solar_output(5) + + var/start_time = world.time + + spawn() + while(world.time <= start_time + duration) + sleep(rad_interval) + radiate() + +/datum/gm_action/solar_storm/get_weight() + return 20 + (metric.count_people_in_department(ROLE_ENGINEERING) * 10) + (metric.count_all_space_mobs() * 30) + +/datum/gm_action/solar_storm/proc/radiate() + // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(!T) + continue + + if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf + continue + + //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. + L.rad_act(rand(15, 30)) + +/datum/gm_action/solar_storm/end() + command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. Please report to medbay if you experience any unusual symptoms. ", "Anomaly Alert") + adjust_solar_output() + length = initial(length) diff --git a/code/modules/gamemaster/actions/spacevine.dm b/code/modules/gamemaster/actions/spacevine.dm new file mode 100644 index 0000000000..c9e5b4135e --- /dev/null +++ b/code/modules/gamemaster/actions/spacevine.dm @@ -0,0 +1,14 @@ +/datum/gm_action/spacevine + name = "space-vine infestation" + departments = list(ROLE_ENGINEERING) + chaotic = 2 + +/datum/gm_action/spacevine/start() + ..() + spacevine_infestation() + +/datum/gm_action/spacevine/announce() + level_seven_announcement() + +/datum/gm_action/spacevine/get_weight() + return 20 + (metric.count_people_in_department(ROLE_ENGINEERING) * 20) + (metric.count_people_in_department(ROLE_EVERYONE) * 10) diff --git a/code/modules/gamemaster/actions/spider_infestation.dm b/code/modules/gamemaster/actions/spider_infestation.dm new file mode 100644 index 0000000000..baba49c2e3 --- /dev/null +++ b/code/modules/gamemaster/actions/spider_infestation.dm @@ -0,0 +1,49 @@ +/datum/gm_action/spider_infestation + name = "spider infestation" + departments = list(ROLE_SECURITY, ROLE_MEDICAL, ROLE_EVERYONE) + chaotic = 30 + + severity = 1 + + var/spawncount = 1 + +/datum/gm_action/spider_infestation/set_up() + spawn(rand(600, 6000)) + announce() + + if(prob(40)) + severity = rand(2,3) + else + severity = 1 + + spawncount = rand(4 * severity, 6 * severity) + +/datum/gm_action/spider_infestation/announce() + command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + + if(severity >= 3) + spawn(rand(600, 3000)) + command_announcement.Announce("Unidentified lifesigns previously detected coming aboard [station_name()] have been classified as a swarm of arachnids. Extreme caution is advised.", "Arachnid Alert") + +/datum/gm_action/spider_infestation/start() + ..() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + if(!temp_vent.welded && temp_vent.network && temp_vent.loc.z in using_map.station_levels) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + + while((spawncount >= 1) && vents.len) + var/obj/vent = pick(vents) + new /obj/effect/spider/spiderling(vent.loc) + vents -= vent + spawncount-- + +/datum/gm_action/spider_infestation/get_weight() + var/security = metric.count_people_in_department(ROLE_SECURITY) + var/medical = metric.count_people_in_department(ROLE_MEDICAL) + var/engineering = metric.count_people_in_department(ROLE_ENGINEERING) + + var/assigned_staff = security + round(medical / 2) + round(engineering / 2) + + return 10 + (assigned_staff * 15) diff --git a/code/modules/gamemaster/actions/spontaneous_appendicitis.dm b/code/modules/gamemaster/actions/spontaneous_appendicitis.dm new file mode 100644 index 0000000000..1758709164 --- /dev/null +++ b/code/modules/gamemaster/actions/spontaneous_appendicitis.dm @@ -0,0 +1,13 @@ +/datum/gm_action/spontaneous_appendicitis + name = "appendicitis" + departments = list(ROLE_MEDICAL, ROLE_EVERYONE) + chaotic = 1 + +/datum/gm_action/spontaneous_appendicitis/start() + ..() + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + if(H.client && !player_is_antag(H.mind) && H.appendicitis()) + break + +/datum/gm_action/spontaneous_appendicitis/get_weight() + return max(0, -5 + (metric.count_people_in_department(ROLE_MEDICAL) * 10)) diff --git a/code/modules/gamemaster/actions/surprise_carp_attack.dm b/code/modules/gamemaster/actions/surprise_carp_attack.dm index 14c4b03e0e..dce6121b61 100644 --- a/code/modules/gamemaster/actions/surprise_carp_attack.dm +++ b/code/modules/gamemaster/actions/surprise_carp_attack.dm @@ -8,14 +8,7 @@ var/mob/living/victim = null /datum/gm_action/surprise_carp_attack/get_weight() - var/people_in_space = 0 - for(var/mob/living/L in player_list) - if(!(L.z in using_map.station_levels)) - continue // Not on the right z-level. - var/turf/T = get_turf(L) - if(istype(T, /turf/space) && istype(T.loc,/area/space)) - people_in_space++ - return people_in_space * 50 + return metric.count_all_space_mobs() * 50 /datum/gm_action/surprise_carp_attack/set_up() var/list/potential_victims = list() @@ -28,7 +21,8 @@ var/turf/T = get_turf(L) if(istype(T, /turf/space) && istype(T.loc,/area/space)) potential_victims.Add(L) - victim = pick(potential_victims) + if(potential_victims.len) + victim = pick(potential_victims) /datum/gm_action/surprise_carp_attack/start() diff --git a/code/modules/gamemaster/actions/viral_infection.dm b/code/modules/gamemaster/actions/viral_infection.dm new file mode 100644 index 0000000000..9b42e2df46 --- /dev/null +++ b/code/modules/gamemaster/actions/viral_infection.dm @@ -0,0 +1,83 @@ +/var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed + +/datum/gm_action/viral_infection + name = "viral infection" + departments = list(ROLE_MEDICAL) + chaotic = 5 + var/list/viruses = list() + severity = 1 + +/datum/gm_action/viral_infection/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 20, + EVENT_LEVEL_MODERATE = 10, + EVENT_LEVEL_MAJOR = 3 + ) + + //generate 1-3 viruses. This way there's an upper limit on how many individual diseases need to be cured if many people are initially infected + var/num_diseases = rand(1,3) + for (var/i=0, i < num_diseases, i++) + var/datum/disease2/disease/D = new /datum/disease2/disease + + var/strength = 1 //whether the disease is of the greater or lesser variety + if (severity >= EVENT_LEVEL_MAJOR && prob(75)) + strength = 2 + D.makerandom(strength) + viruses += D + +/datum/gm_action/viral_infection/announce() + var/level + if (severity == EVENT_LEVEL_MUNDANE) + return + else if (severity == EVENT_LEVEL_MODERATE) + level = pick("one", "two", "three", "four") + else + level = "five" + + spawn(rand(0, 3000)) + if(severity == EVENT_LEVEL_MAJOR || prob(60)) + command_announcement.Announce("Confirmed outbreak of level [level] biohazard aboard \the [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak5.ogg') + +/datum/gm_action/viral_infection/start() + if(!viruses.len) return + + ..() + + var/list/candidates = list() //list of candidate keys + for(var/mob/living/carbon/human/G in player_list) + if(G.mind && G.stat != DEAD && G.is_client_active(5) && !player_is_antag(G.mind)) + var/turf/T = get_turf(G) + if(T.z in using_map.station_levels) + candidates += G + if(!candidates.len) return + candidates = shuffle(candidates)//Incorporating Donkie's list shuffle + + var/list/used_viruses = list() + var/list/used_candidates = list() + severity = max(EVENT_LEVEL_MUNDANE, severity - 1) + var/actual_severity = severity * rand(1, 3) + while(actual_severity > 0 && candidates.len) + var/datum/disease2/disease/D = pick(viruses) + infect_mob(candidates[1], D.getcopy()) + used_candidates += candidates[1] + candidates.Remove(candidates[1]) + actual_severity-- + used_viruses |= D + + event_viruses |= used_viruses + var/list/used_viruses_links = list() + var/list/used_viruses_text = list() + for(var/datum/disease2/disease/D in used_viruses) + used_viruses_links += "[D.name()]" + used_viruses_text += D.name() + + var/list/used_candidates_links = list() + var/list/used_candidates_text = list() + for(var/mob/M in used_candidates) + used_candidates_links += key_name_admin(M) + used_candidates_text += key_name(M) + + log_admin("Virus event affecting [english_list(used_candidates_text)] started; Viruses: [english_list(used_viruses_text)]") + message_admins("Virus event affecting [english_list(used_candidates_links)] started; Viruses: [english_list(used_viruses_links)]") + +/datum/gm_action/viral_infection/get_weight() + return (metric.count_people_in_department(ROLE_MEDICAL) * 20) diff --git a/code/modules/gamemaster/actions/viral_outbreak.dm b/code/modules/gamemaster/actions/viral_outbreak.dm new file mode 100644 index 0000000000..f0eb27b41a --- /dev/null +++ b/code/modules/gamemaster/actions/viral_outbreak.dm @@ -0,0 +1,44 @@ +/datum/gm_action/viral_outbreak + name = "viral outbreak" + departments = list(ROLE_MEDICAL, ROLE_EVERYONE) + chaotic = 30 + severity = 1 + var/list/candidates = list() + +/datum/gm_action/viral_outbreak/set_up() + candidates.Cut() // Incase we somehow get run twice. + severity = rand(2, 4) + for(var/mob/living/carbon/human/G in player_list) + if(G.client && G.stat != DEAD) + candidates += G + if(!candidates.len) return + candidates = shuffle(candidates)//Incorporating Donkie's list shuffle + +/datum/gm_action/viral_outbreak/announce() + command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') + +/datum/gm_action/viral_outbreak/start() + ..() + while(severity > 0 && candidates.len) + if(prob(33)) + infect_mob_random_lesser(candidates[1]) + else + infect_mob_random_greater(candidates[1]) + + candidates.Remove(candidates[1]) + severity-- + +/datum/gm_action/viral_outbreak/get_weight() + var/medical = metric.count_people_in_department(ROLE_MEDICAL) + var/security = metric.count_people_in_department(ROLE_SECURITY) + var/everyone = metric.count_people_in_department(ROLE_EVERYONE) + + var/assigned_staff = medical + round(security / 2) + + if(!medical) // No docs, no staff. + assigned_staff = 0 + + if(assigned_staff < round(everyone / 6) - assigned_staff) // A doc or half an officer per roughly six people. Any less, and assigned staff is halved. + assigned_staff /= 2 + + return (assigned_staff * 10) diff --git a/code/modules/gamemaster/actions/wallrot.dm b/code/modules/gamemaster/actions/wallrot.dm new file mode 100644 index 0000000000..e2c05aed74 --- /dev/null +++ b/code/modules/gamemaster/actions/wallrot.dm @@ -0,0 +1,43 @@ +/datum/gm_action/wallrot + name = "wall rot" + departments = list(ROLE_ENGINEERING) + reusable = TRUE + var/turf/simulated/wall/center + severity = 1 + +/datum/gm_action/wallrot/set_up() + severity = rand(1,3) + center = null + // 100 attempts + for(var/i=0, i<100, i++) + var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), 1) + if(istype(candidate, /turf/simulated/wall)) + center = candidate + return 1 + return 0 + +/datum/gm_action/wallrot/announce() + if(center && prob(min(90,40 * severity))) + command_announcement.Announce("Harmful fungi detected on \the [station_name()] nearby [center.loc.name]. Station structures may be contaminated.", "Biohazard Alert") + +/datum/gm_action/wallrot/start() + ..() + spawn() + if(center) + // Make sure at least one piece of wall rots! + center.rot() + + // Have a chance to rot lots of other walls. + var/rotcount = 0 + var/actual_severity = severity * rand(5, 10) + for(var/turf/simulated/wall/W in range(5, center)) + if(prob(50)) + W.rot() + rotcount++ + + // Only rot up to severity walls + if(rotcount >= actual_severity) + break + +/datum/gm_action/wallrot/get_weight() + return 60 + (metric.count_people_in_department(ROLE_ENGINEERING) * 35) diff --git a/code/modules/gamemaster/actions/window_break.dm b/code/modules/gamemaster/actions/window_break.dm new file mode 100644 index 0000000000..3cb16cba58 --- /dev/null +++ b/code/modules/gamemaster/actions/window_break.dm @@ -0,0 +1,78 @@ +/datum/gm_action/window_break + name = "window breach" + departments = list(ROLE_ENGINEERING) + chaotic = 5 + var/obj/structure/window/chosen_window + var/list/obj/structure/window/collateral_windows + var/turf/chosen_location + var/list/area/excluded = list( + /area/shuttle, + /area/crew_quarters + ) + +/datum/gm_action/window_break/set_up() + var/list/area/grand_list_of_areas = get_station_areas(excluded) + //try 10 times + for(var/i in 1 to 10) + var/area/A = pick(grand_list_of_areas) + var/list/obj/structure/window/possible_target_windows = list() + for(var/obj/structure/window/target_window in A.contents) + possible_target_windows += target_window + possible_target_windows = shuffle(possible_target_windows) + + for(var/obj/structure/window/target_window in possible_target_windows) + //if() don't have any conditions, for isn't strictly necessary + chosen_window = target_window + chosen_location = chosen_window.loc + collateral_windows = gather_collateral_windows(chosen_window) + return + +//TL;DR: breadth first search for all connected turfs with windows +/datum/gm_action/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) + var/list/turf/frontier_set = list(target_window.loc) + var/list/obj/structure/window/result_set = list() + var/list/turf/explored_set = list() + + while(frontier_set.len > 0) + var/turf/current = frontier_set[1] + frontier_set -= current + explored_set += current + + var/contains_windows = 0 + for(var/obj/structure/window/to_add in current.contents) + contains_windows = 1 + result_set += to_add + + if(contains_windows) + //add adjacent turfs to be checked for windows as well + var/turf/neighbor = locate(current.x + 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x - 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y + 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y - 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + return result_set + +/datum/gm_action/window_break/start() + if(!chosen_window) + return + ..() + chosen_window.shatter(0) + + spawn() + for(var/obj/structure/window/current_collateral in collateral_windows) + sleep(rand(1,20)) + current_collateral.take_damage(current_collateral.health - (current_collateral.maxhealth / 5)) //set to 1/5th health + +/datum/gm_action/window_break/announce() + if(chosen_window) + command_announcement.Announce("Structural integrity of windows at [chosen_location.loc.name] is failing. Immediate repair or replacement is advised.", "Structural Alert") + +/datum/gm_action/window_break/get_weight() + return 20 * metric.count_people_in_department(ROLE_ENGINEERING) diff --git a/code/modules/gamemaster/actions/wormholes.dm b/code/modules/gamemaster/actions/wormholes.dm new file mode 100644 index 0000000000..f2a90f5539 --- /dev/null +++ b/code/modules/gamemaster/actions/wormholes.dm @@ -0,0 +1,23 @@ +/datum/gm_action/wormholes + name = "space-time anomalies" + chaotic = 70 + length = 12 MINUTES + departments = list(ROLE_EVERYONE) + severity = 1 + +/datum/gm_action/wormholes/set_up() // 1 out of 5 will be full-duration wormholes, meaning up to a minute long. + severity = pickweight(list( + 3 = 5, + 2 = 7, + 1 = 13 + )) + +/datum/gm_action/wormholes/start() + ..() + wormhole_event(length / 2, (severity / 3)) + +/datum/gm_action/wormholes/get_weight() + return 10 + max(0, -30 + (metric.count_people_in_department(ROLE_EVERYONE) * 5) + (metric.count_people_in_department(ROLE_ENGINEERING) + 10) + (metric.count_people_in_department(ROLE_MEDICAL) * 20)) + +/datum/gm_action/wormholes/end() + command_announcement.Announce("There are no more space-time anomalies detected on the station.", "Anomaly Alert") diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 22b4b2e6c6..c1c7ebedaf 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -126,6 +126,38 @@ slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', ) item_state = "boxing" + special_attack_type = /datum/unarmed_attack/holopugilism + +datum/unarmed_attack/holopugilism + sparring_variant_type = /datum/unarmed_attack/holopugilism + +datum/unarmed_attack/holopugilism/unarmed_override(var/mob/living/carbon/human/user,var/mob/living/carbon/human/target,var/zone) + user.do_attack_animation(src) + var/damage = rand(0, 9) + if(!damage) + playsound(target.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + target.visible_message("[user] has attempted to punch [target]!") + return TRUE + var/obj/item/organ/external/affecting = target.get_organ(ran_zone(user.zone_sel.selecting)) + var/armor_block = target.run_armor_check(affecting, "melee") + var/armor_soak = target.get_armor_soak(affecting, "melee") + + if(HULK in user.mutations) + damage += 5 + + playsound(target.loc, "punch", 25, 1, -1) + + target.visible_message("[user] has punched [target]!") + + if(armor_soak >= damage) + return TRUE + + target.apply_damage(damage, HALLOSS, affecting, armor_block, armor_soak) + if(damage >= 9) + target.visible_message("[user] has weakened [target]!") + target.apply_effect(4, WEAKEN, armor_block) + + return TRUE /obj/structure/window/reinforced/holowindow/attackby(obj/item/W as obj, mob/user as mob) if(!istype(W)) @@ -262,8 +294,8 @@ spark_system.set_up(5, 0, user.loc) spark_system.start() playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - return 0 + return TRUE + return FALSE /obj/item/weapon/holo/esword/New() item_color = pick("red","blue","green","purple") diff --git a/code/modules/hydroponics/beekeeping/beehive.dm b/code/modules/hydroponics/beekeeping/beehive.dm index 7af5ce286c..d215ab3dac 100644 --- a/code/modules/hydroponics/beekeeping/beehive.dm +++ b/code/modules/hydroponics/beekeeping/beehive.dm @@ -241,6 +241,8 @@ icon = 'icons/obj/beekeeping.dmi' icon_state = "wax" default_type = "wax" + pass_color = TRUE + strict_color_stacking = TRUE /obj/item/stack/material/wax/New() ..() @@ -252,6 +254,7 @@ icon_colour = "#fff343" melting_point = T0C+300 weight = 1 + pass_stack_colors = TRUE var/global/list/datum/stack_recipe/wax_recipes = list( \ new/datum/stack_recipe("candle", /obj/item/weapon/flame/candle) \ diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 48ac62af07..963c5d1cc5 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -5,6 +5,7 @@ icon = 'icons/obj/hydroponics_products.dmi' icon_state = "blank" desc = "Nutritious! Probably." + flags = NOCONDUCT slot_flags = SLOT_HOLSTER var/plantname @@ -62,6 +63,8 @@ update_desc() if(reagents.total_volume > 0) bitesize = 1+round(reagents.total_volume / 2, 1) + if(seed.get_trait(TRAIT_STINGS)) + force = 1 /obj/item/weapon/reagent_containers/food/snacks/grown/proc/update_desc() diff --git a/code/modules/hydroponics/grown_inedible.dm b/code/modules/hydroponics/grown_inedible.dm index aa054cae29..2b02116478 100644 --- a/code/modules/hydroponics/grown_inedible.dm +++ b/code/modules/hydroponics/grown_inedible.dm @@ -37,6 +37,7 @@ desc = "A reminder of meals gone by." icon = 'icons/obj/trash.dmi' icon_state = "corncob" + flags = NOCONDUCT w_class = ITEMSIZE_SMALL throwforce = 0 throw_speed = 4 @@ -55,6 +56,7 @@ desc = "A peel from a banana." icon = 'icons/obj/items.dmi' icon_state = "banana_peel" + flags = NOCONDUCT w_class = ITEMSIZE_SMALL throwforce = 0 throw_speed = 4 diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 9ed2d836a6..b2d4443c9c 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -21,7 +21,9 @@ var/kitchen_tag // Used by the reagent grinder. var/trash_type // Garbage item produced when eaten. var/splat_type = /obj/effect/decal/cleanable/fruit_smudge // Graffiti decal. - var/has_mob_product + var/has_mob_product // Mob products. (Dionaea, Walking Mushrooms, Angry Tomatoes) + var/apply_color_to_mob = TRUE // Do we color the mob to match the plant? + var/has_item_product // Item products. (Eggy) var/force_layer /datum/seed/New() @@ -619,6 +621,7 @@ gene.values["[TRAIT_CONSUME_GASSES]"] = null if(GENE_METABOLISM) has_mob_product = gene.values["mob_product"] + has_item_product = gene.values["item_product"] gene.values["mob_product"] = null for(var/trait in gene.values) @@ -649,6 +652,7 @@ traits_to_copy = list(TRAIT_TOXINS_TOLERANCE,TRAIT_PEST_TOLERANCE,TRAIT_WEED_TOLERANCE,TRAIT_ENDURANCE) if(GENE_METABOLISM) P.values["mob_product"] = has_mob_product + P.values["item_product"] = has_item_product traits_to_copy = list(TRAIT_REQUIRES_NUTRIENTS,TRAIT_REQUIRES_WATER,TRAIT_ALTER_TEMP) if(GENE_VIGOUR) traits_to_copy = list(TRAIT_PRODUCTION,TRAIT_MATURATION,TRAIT_YIELD,TRAIT_SPREAD) @@ -709,6 +713,8 @@ var/obj/item/product if(has_mob_product) product = new has_mob_product(get_turf(user),name) + else if(has_item_product) + product = new has_item_product(get_turf(user)) else product = new /obj/item/weapon/reagent_containers/food/snacks/grown(get_turf(user),name) if(get_trait(TRAIT_PRODUCT_COLOUR)) @@ -728,6 +734,9 @@ clr = get_trait(TRAIT_BIOLUM_COLOUR) product.set_light(get_trait(TRAIT_BIOLUM), l_color = clr) + if(get_trait(TRAIT_STINGS)) + product.force = 1 + //Handle spawning in living, mobile products (like dionaea). if(istype(product,/mob/living)) product.visible_message("The pod disgorges [product]!") @@ -749,6 +758,8 @@ new_seed.kitchen_tag = kitchen_tag new_seed.trash_type = trash_type new_seed.has_mob_product = has_mob_product + new_seed.has_item_product = has_item_product + //Copy over everything else. if(mutants) new_seed.mutants = mutants.Copy() if(chems) new_seed.chems = chems.Copy() diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index eab78597b1..9e34611b7d 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -3,9 +3,9 @@ name = "chili" seed_name = "chili" display_name = "chili plants" + kitchen_tag = "chili" chems = list("capsaicin" = list(3,5), "nutriment" = list(1,25)) mutants = list("icechili") - kitchen_tag = "chili" /datum/seed/chili/New() ..() @@ -24,9 +24,9 @@ name = "icechili" seed_name = "ice pepper" display_name = "ice-pepper plants" + kitchen_tag = "icechili" mutants = null chems = list("frostoil" = list(3,5), "nutriment" = list(1,50)) - kitchen_tag = "icechili" /datum/seed/chili/ice/New() ..() @@ -39,9 +39,9 @@ name = "berries" seed_name = "berry" display_name = "berry bush" + kitchen_tag = "berries" mutants = list("glowberries","poisonberries") chems = list("nutriment" = list(1,10), "berryjuice" = list(10,10)) - kitchen_tag = "berries" /datum/seed/berry/New() ..() @@ -112,7 +112,6 @@ mutants = list("deathnettle") chems = list("nutriment" = list(1,50), "sacid" = list(0,1)) kitchen_tag = "nettle" - kitchen_tag = "nettle" /datum/seed/nettle/New() ..() @@ -130,9 +129,9 @@ name = "deathnettle" seed_name = "death nettle" display_name = "death nettles" + kitchen_tag = "deathnettle" mutants = null chems = list("nutriment" = list(1,50), "pacid" = list(0,1)) - kitchen_tag = "deathnettle" /datum/seed/nettle/death/New() ..() @@ -222,9 +221,9 @@ name = "eggplant" seed_name = "eggplant" display_name = "eggplants" - mutants = list("realeggplant") - chems = list("nutriment" = list(1,10)) kitchen_tag = "eggplant" + mutants = list("egg-plant") + chems = list("nutriment" = list(1,10)) /datum/seed/eggplant/New() ..() @@ -239,14 +238,24 @@ set_trait(TRAIT_IDEAL_HEAT, 298) set_trait(TRAIT_IDEAL_LIGHT, 7) +// Return of Eggy. Just makes purple eggs. If the reagents are separated from the egg production by xenobotany or RNG, it's still an Egg plant. +/datum/seed/eggplant/egg + name = "egg-plant" + seed_name = "egg-plant" + display_name = "egg-plants" + kitchen_tag = "egg-plant" + mutants = null + chems = list("nutriment" = list(1,5), "egg" = list(3,12)) + has_item_product = /obj/item/weapon/reagent_containers/food/snacks/egg/purple + //Apples/varieties. /datum/seed/apple name = "apple" seed_name = "apple" display_name = "apple tree" + kitchen_tag = "apple" mutants = list("poisonapple","goldapple") chems = list("nutriment" = list(1,10),"applejuice" = list(10,20)) - kitchen_tag = "apple" /datum/seed/apple/New() ..() @@ -270,9 +279,9 @@ name = "goldapple" seed_name = "golden apple" display_name = "gold apple tree" + kitchen_tag = "goldapple" mutants = null chems = list("nutriment" = list(1,10), "gold" = list(1,5)) - kitchen_tag = "goldapple" /datum/seed/apple/gold/New() ..() @@ -287,9 +296,9 @@ name = "ambrosia" seed_name = "ambrosia vulgaris" display_name = "ambrosia vulgaris" + kitchen_tag = "ambrosia" mutants = list("ambrosiadeus") chems = list("nutriment" = list(1), "space_drugs" = list(1,8), "kelotane" = list(1,8,1), "bicaridine" = list(1,10,1), "toxin" = list(1,10)) - kitchen_tag = "ambrosia" /datum/seed/ambrosia/New() ..() @@ -307,9 +316,9 @@ name = "ambrosiadeus" seed_name = "ambrosia deus" display_name = "ambrosia deus" + kitchen_tag = "ambrosiadeus" mutants = null chems = list("nutriment" = list(1), "bicaridine" = list(1,8), "synaptizine" = list(1,8,1), "hyperzine" = list(1,10,1), "space_drugs" = list(1,10)) - kitchen_tag = "ambrosiadeus" /datum/seed/ambrosia/deus/New() ..() @@ -450,6 +459,7 @@ display_name = "tower caps" chems = list("woodpulp" = list(10,1)) mutants = null + has_item_product = /obj/item/stack/material/log /datum/seed/mushroom/towercap/New() ..() @@ -502,6 +512,7 @@ name = "harebells" seed_name = "harebell" display_name = "harebells" + kitchen_tag = "harebell" chems = list("nutriment" = list(1,20)) /datum/seed/flower/New() @@ -519,8 +530,8 @@ name = "poppies" seed_name = "poppy" display_name = "poppies" - chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) kitchen_tag = "poppy" + chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) /datum/seed/flower/poppy/New() ..() @@ -539,6 +550,7 @@ name = "sunflowers" seed_name = "sunflower" display_name = "sunflowers" + kitchen_tag = "sunflower" /datum/seed/flower/sunflower/New() ..() @@ -554,6 +566,7 @@ name = "lavender" seed_name = "lavender" display_name = "lavender" + kitchen_tag = "lavender" chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) /datum/seed/flower/lavender/New() @@ -569,11 +582,46 @@ set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.05) set_trait(TRAIT_WATER_CONSUMPTION, 0.5) +/datum/seed/flower/rose + name = "rose" + seed_name = "rose" + display_name = "rose" + kitchen_tag = "rose" + mutants = list("bloodrose") + chems = list("nutriment" = list(1,5), "stoxin" = list(0,2)) + +/datum/seed/flower/rose/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_ICON,"flowers") + set_trait(TRAIT_PRODUCT_COLOUR,"#ce0e0e") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"bush5") + set_trait(TRAIT_IDEAL_LIGHT, 7) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.1) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + set_trait(TRAIT_STINGS,1) + +/datum/seed/flower/rose/blood + name = "bloodrose" + display_name = "bleeding rose" + mutants = null + chems = list("nutriment" = list(1,5), "stoxin" = list(1,5), "blood" = list(0,2)) + +/datum/seed/flower/rose/blood/New() + ..() + set_trait(TRAIT_IDEAL_LIGHT, 1) + set_trait(TRAIT_PLANT_COLOUR,"#5e0303") + set_trait(TRAIT_CARNIVOROUS,1) + //Grapes/varieties /datum/seed/grapes name = "grapes" seed_name = "grape" display_name = "grapevines" + kitchen_tag = "grapes" mutants = list("greengrapes") chems = list("nutriment" = list(1,10), "sugar" = list(1,5), "grapejuice" = list(10,10)) @@ -602,12 +650,103 @@ ..() set_trait(TRAIT_PRODUCT_COLOUR,"42ed2f") +// Lettuce/varieties. +/datum/seed/lettuce + name = "lettuce" + seed_name = "lettuce" + display_name = "lettuce" + kitchen_tag = "cabbage" + chems = list("nutriment" = list(1,15)) + +/datum/seed/lettuce/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,4) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_POTENCY,8) + set_trait(TRAIT_PRODUCT_ICON,"lettuce") + set_trait(TRAIT_PRODUCT_COLOUR,"#A8D0A7") + set_trait(TRAIT_PLANT_COLOUR,"#6D9C6B") + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 8) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.13) + +/datum/seed/lettuce/ice + name = "siflettuce" + seed_name = "glacial lettuce" + display_name = "glacial lettuce" + kitchen_tag = "icelettuce" + chems = list("nutriment" = list(1,5), "paracetamol" = list(0,2)) + +/datum/seed/lettuce/ice/New() + ..() + set_trait(TRAIT_ALTER_TEMP, -5) + set_trait(TRAIT_PRODUCT_COLOUR,"#9ABCC9") + +//Wabback / varieties. +/datum/seed/wabback + name = "whitewabback" + seed_name = "white wabback" + seed_noun = "nodes" + display_name = "white wabback" + chems = list("nutriment" = list(1,10), "protein" = list(1,5), "enzyme" = list(0,3)) + kitchen_tag = "wabback" + mutants = list("blackwabback","wildwabback") + has_item_product = /obj/item/stack/material/cloth + +/datum/seed/wabback/New() + ..() + set_trait(TRAIT_IDEAL_LIGHT, 5) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,3) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,5) + set_trait(TRAIT_PRODUCT_ICON,"carrot2") + set_trait(TRAIT_PRODUCT_COLOUR,"#E6EDFA") + set_trait(TRAIT_PLANT_ICON,"chute") + set_trait(TRAIT_PLANT_COLOUR, "#0650ce") + set_trait(TRAIT_WATER_CONSUMPTION, 10) + set_trait(TRAIT_ALTER_TEMP, -1) + set_trait(TRAIT_CARNIVOROUS,1) + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_SPREAD,1) + +/datum/seed/wabback/vine + name = "blackwabback" + seed_name = "black wabback" + display_name = "black wabback" + mutants = null + chems = list("nutriment" = list(1,3), "protein" = list(1,10), "serotrotium_v" = list(0,1)) + +/datum/seed/wabback/vine/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#2E2F32") + set_trait(TRAIT_CARNIVOROUS,2) + +/datum/seed/wabback/wild + name = "wildwabback" + seed_name = "wild wabback" + display_name = "wild wabback" + mutants = list("whitewabback") + has_item_product = null + chems = list("nutriment" = list(1,15), "protein" = list(0,2), "enzyme" = list(0,1)) + +/datum/seed/wabback/wild/New() + ..() + set_trait(TRAIT_IDEAL_LIGHT, 3) + set_trait(TRAIT_WATER_CONSUMPTION, 7) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.1) + set_trait(TRAIT_YIELD,5) + //Everything else /datum/seed/peanuts name = "peanut" seed_name = "peanut" display_name = "peanut vines" - chems = list("nutriment" = list(1,10)) + kitchen_tag = "peanut" + chems = list("nutriment" = list(1,10), "peanutoil" = list(1,3)) /datum/seed/peanuts/New() ..() @@ -621,12 +760,32 @@ set_trait(TRAIT_PLANT_ICON,"bush2") set_trait(TRAIT_IDEAL_LIGHT, 6) +/datum/seed/vanilla + name = "vanilla" + seed_name = "vanilla" + display_name = "vanilla" + kitchen_tag = "vanilla" + chems = list("nutriment" = list(1,10), "vanilla" = list(0,3), "sugar" = list(0, 1)) + +/datum/seed/vanilla/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_ICON,"chili") + set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"bush5") + set_trait(TRAIT_IDEAL_LIGHT, 8) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.3) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + /datum/seed/cabbage name = "cabbage" seed_name = "cabbage" display_name = "cabbages" - chems = list("nutriment" = list(1,10)) kitchen_tag = "cabbage" + chems = list("nutriment" = list(1,10)) /datum/seed/cabbage/New() ..() @@ -647,9 +806,9 @@ name = "banana" seed_name = "banana" display_name = "banana tree" + kitchen_tag = "banana" chems = list("banana" = list(10,10)) trash_type = /obj/item/weapon/bananapeel - kitchen_tag = "banana" /datum/seed/banana/New() ..() @@ -669,8 +828,8 @@ name = "corn" seed_name = "corn" display_name = "ears of corn" - chems = list("nutriment" = list(1,10), "cornoil" = list(1,10)) kitchen_tag = "corn" + chems = list("nutriment" = list(1,10), "cornoil" = list(1,10)) trash_type = /obj/item/weapon/corncob /datum/seed/corn/New() @@ -691,8 +850,8 @@ name = "potato" seed_name = "potato" display_name = "potatoes" - chems = list("nutriment" = list(1,10), "potatojuice" = list(10,10)) kitchen_tag = "potato" + chems = list("nutriment" = list(1,10), "potatojuice" = list(10,10)) /datum/seed/potato/New() ..() @@ -707,29 +866,29 @@ set_trait(TRAIT_WATER_CONSUMPTION, 6) /datum/seed/onion - name = "onion" - seed_name = "onion" - display_name = "onions" - chems = list("nutriment" = list(1,10)) - kitchen_tag = "onion" + name = "onion" + seed_name = "onion" + display_name = "onions" + kitchen_tag = "onion" + chems = list("nutriment" = list(1,10)) /datum/seed/onion/New() - ..() - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"onion") - set_trait(TRAIT_PRODUCT_COLOUR,"#E0C367") - set_trait(TRAIT_PLANT_ICON,"carrot") - set_trait(TRAIT_WATER_CONSUMPTION, 6) + ..() + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"onion") + set_trait(TRAIT_PRODUCT_COLOUR,"#E0C367") + set_trait(TRAIT_PLANT_ICON,"carrot") + set_trait(TRAIT_WATER_CONSUMPTION, 6) /datum/seed/soybean name = "soybean" seed_name = "soybean" display_name = "soybeans" - chems = list("nutriment" = list(1,20), "soymilk" = list(10,20)) kitchen_tag = "soybeans" + chems = list("nutriment" = list(1,20), "soymilk" = list(10,20)) /datum/seed/soybean/New() ..() @@ -746,8 +905,8 @@ name = "wheat" seed_name = "wheat" display_name = "wheat stalks" - chems = list("nutriment" = list(1,25), "flour" = list(15,15)) kitchen_tag = "wheat" + chems = list("nutriment" = list(1,25), "flour" = list(15,15)) /datum/seed/wheat/New() ..() @@ -766,8 +925,8 @@ name = "rice" seed_name = "rice" display_name = "rice stalks" - chems = list("nutriment" = list(1,25), "rice" = list(10,15)) kitchen_tag = "rice" + chems = list("nutriment" = list(1,25), "rice" = list(10,15)) /datum/seed/rice/New() ..() @@ -786,8 +945,8 @@ name = "carrot" seed_name = "carrot" display_name = "carrots" - chems = list("nutriment" = list(1,20), "imidazoline" = list(3,5), "carrotjuice" = list(10,20)) kitchen_tag = "carrot" + chems = list("nutriment" = list(1,20), "imidazoline" = list(3,5), "carrotjuice" = list(10,20)) /datum/seed/carrots/New() ..() @@ -821,8 +980,8 @@ name = "whitebeet" seed_name = "white-beet" display_name = "white-beets" - chems = list("nutriment" = list(0,20), "sugar" = list(1,5)) kitchen_tag = "whitebeet" + chems = list("nutriment" = list(0,20), "sugar" = list(1,5)) /datum/seed/whitebeets/New() ..() @@ -840,6 +999,7 @@ name = "sugarcane" seed_name = "sugarcane" display_name = "sugarcanes" + kitchen_tag = "sugarcanes" chems = list("sugar" = list(4,5)) /datum/seed/sugarcane/New() @@ -855,12 +1015,93 @@ set_trait(TRAIT_PLANT_ICON,"stalk3") set_trait(TRAIT_IDEAL_HEAT, 298) +/datum/seed/rhubarb + name = "rhubarb" + seed_name = "rhubarb" + display_name = "rhubarb" + kitchen_tag = "rhubarb" + chems = list("nutriment" = list(1,15)) + +/datum/seed/rhubarb/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_POTENCY,6) + set_trait(TRAIT_PRODUCT_ICON,"stalk") + set_trait(TRAIT_PRODUCT_COLOUR,"#FD5656") + set_trait(TRAIT_PLANT_ICON,"stalk3") + +/datum/seed/celery + name = "celery" + seed_name = "celery" + display_name = "celery" + kitchen_tag = "celery" + chems = list("nutriment" = list(5,20)) + +/datum/seed/celery/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,4) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,8) + set_trait(TRAIT_PRODUCT_ICON,"stalk") + set_trait(TRAIT_PRODUCT_COLOUR,"#56FD56") + set_trait(TRAIT_PLANT_ICON,"stalk3") + +/datum/seed/spineapple + name = "spineapple" + seed_name = "spineapple" + display_name = "spineapple" + kitchen_tag = "pineapple" + chems = list("nutriment" = list(1,5), "enzyme" = list(1,5), "pineapplejuice" = list(1, 20)) + +/datum/seed/spineapple/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,1) + set_trait(TRAIT_POTENCY,13) + set_trait(TRAIT_PRODUCT_ICON,"pineapple") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFF23B") + set_trait(TRAIT_PLANT_COLOUR,"#87C969") + set_trait(TRAIT_PLANT_ICON,"corn") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 4) + set_trait(TRAIT_WATER_CONSUMPTION, 8) + set_trait(TRAIT_STINGS,1) + +/datum/seed/durian + name = "durian" + seed_name = "durian" + seed_noun = "pits" + display_name = "durian" + kitchen_tag = "durian" + chems = list("nutriment" = list(1,5), "durianpaste" = list(1, 20)) + +/datum/seed/durian/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"spinefruit") + set_trait(TRAIT_PRODUCT_COLOUR,"#757631") + set_trait(TRAIT_PLANT_COLOUR,"#87C969") + set_trait(TRAIT_PLANT_ICON,"tree") + set_trait(TRAIT_IDEAL_LIGHT, 8) + set_trait(TRAIT_WATER_CONSUMPTION, 8) + /datum/seed/watermelon name = "watermelon" seed_name = "watermelon" display_name = "watermelon vine" - chems = list("nutriment" = list(1,6), "watermelonjuice" = list(10,6)) kitchen_tag = "watermelon" + chems = list("nutriment" = list(1,6), "watermelonjuice" = list(10,6)) /datum/seed/watermelon/New() ..() @@ -883,8 +1124,8 @@ name = "pumpkin" seed_name = "pumpkin" display_name = "pumpkin vine" - chems = list("nutriment" = list(1,6)) kitchen_tag = "pumpkin" + chems = list("nutriment" = list(1,6)) /datum/seed/pumpkin/New() ..() @@ -903,8 +1144,8 @@ name = "lime" seed_name = "lime" display_name = "lime trees" - chems = list("nutriment" = list(1,20), "limejuice" = list(10,20)) kitchen_tag = "lime" + chems = list("nutriment" = list(1,20), "limejuice" = list(10,20)) /datum/seed/citrus/New() ..() @@ -923,8 +1164,8 @@ name = "lemon" seed_name = "lemon" display_name = "lemon trees" - chems = list("nutriment" = list(1,20), "lemonjuice" = list(10,20)) kitchen_tag = "lemon" + chems = list("nutriment" = list(1,20), "lemonjuice" = list(10,20)) /datum/seed/citrus/lemon/New() ..() @@ -950,8 +1191,8 @@ name = "grass" seed_name = "grass" display_name = "grass" - chems = list("nutriment" = list(1,20)) kitchen_tag = "grass" + chems = list("nutriment" = list(1,20)) /datum/seed/grass/New() ..() @@ -970,6 +1211,7 @@ name = "cocoa" seed_name = "cacao" display_name = "cacao tree" + kitchen_tag = "cocoa" chems = list("nutriment" = list(1,10), "coco" = list(4,5)) /datum/seed/cocoa/New() @@ -990,8 +1232,8 @@ seed_name = "cherry" seed_noun = "pits" display_name = "cherry tree" - chems = list("nutriment" = list(1,15), "sugar" = list(1,15), "cherryjelly" = list(10,15)) kitchen_tag = "cherries" + chems = list("nutriment" = list(1,15), "sugar" = list(1,15), "cherryjelly" = list(10,15)) /datum/seed/cherries/New() ..() @@ -1010,6 +1252,7 @@ name = "kudzu" seed_name = "kudzu" display_name = "kudzu vines" + kitchen_tag = "kudzu" chems = list("nutriment" = list(1,50), "anti_toxin" = list(1,25)) /datum/seed/kudzu/New() @@ -1031,6 +1274,7 @@ seed_noun = "nodes" display_name = "replicant pods" can_self_harvest = 1 + apply_color_to_mob = FALSE has_mob_product = /mob/living/carbon/alien/diona /datum/seed/diona/New() @@ -1050,8 +1294,8 @@ name = "shand" seed_name = "Selem's hand" display_name = "Selem's hand leaves" - chems = list("bicaridine" = list(0,10)) kitchen_tag = "shand" + chems = list("bicaridine" = list(0,10)) /datum/seed/shand/New() ..() @@ -1070,8 +1314,8 @@ name = "mtear" seed_name = "Malani's tear" display_name = "Malani's tear leaves" - chems = list("honey" = list(1,10), "kelotane" = list(3,5)) kitchen_tag = "mtear" + chems = list("honey" = list(1,10), "kelotane" = list(3,5)) /datum/seed/mtear/New() ..() @@ -1090,11 +1334,13 @@ name = "telriis" seed_name = "telriis" display_name = "telriis grass" + kitchen_tag = "telriis" chems = list("pwine" = list(1,5), "nutriment" = list(1,6)) /datum/seed/telriis/New() ..() - set_trait(TRAIT_PLANT_ICON,"telriis") + set_trait(TRAIT_PLANT_ICON,"ambrosia") + set_trait(TRAIT_PRODUCT_ICON,"ambrosia") set_trait(TRAIT_ENDURANCE,50) set_trait(TRAIT_MATURATION,5) set_trait(TRAIT_PRODUCTION,5) @@ -1105,11 +1351,13 @@ name = "thaadra" seed_name = "thaa'dra" display_name = "thaa'dra lichen" + kitchen_tag = "thaadra" chems = list("frostoil" = list(1,5),"nutriment" = list(1,5)) /datum/seed/thaadra/New() ..() - set_trait(TRAIT_PLANT_ICON,"thaadra") + set_trait(TRAIT_PLANT_ICON,"grass") + set_trait(TRAIT_PLANT_COLOUR,"#ABC7D2") set_trait(TRAIT_ENDURANCE,10) set_trait(TRAIT_MATURATION,5) set_trait(TRAIT_PRODUCTION,9) @@ -1120,11 +1368,12 @@ name = "jurlmah" seed_name = "jurl'mah" display_name = "jurl'mah reeds" + kitchen_tag = "jurlmah" chems = list("serotrotium" = list(1,5),"nutriment" = list(1,5)) /datum/seed/jurlmah/New() ..() - set_trait(TRAIT_PLANT_ICON,"jurlmah") + set_trait(TRAIT_PLANT_ICON,"mushroom9") set_trait(TRAIT_ENDURANCE,12) set_trait(TRAIT_MATURATION,8) set_trait(TRAIT_PRODUCTION,9) @@ -1135,11 +1384,12 @@ name = "amauri" seed_name = "amauri" display_name = "amauri plant" + kitchen_tag = "amauri" chems = list("zombiepowder" = list(1,10),"condensedcapsaicin" = list(1,5),"nutriment" = list(1,5)) /datum/seed/amauri/New() ..() - set_trait(TRAIT_PLANT_ICON,"amauri") + set_trait(TRAIT_PLANT_ICON,"bush4") set_trait(TRAIT_ENDURANCE,10) set_trait(TRAIT_MATURATION,8) set_trait(TRAIT_PRODUCTION,9) @@ -1150,11 +1400,12 @@ name = "gelthi" seed_name = "gelthi" display_name = "gelthi plant" + kitchen_tag = "gelthi" chems = list("stoxin" = list(1,5),"capsaicin" = list(1,5),"nutriment" = list(1,5)) /datum/seed/gelthi/New() ..() - set_trait(TRAIT_PLANT_ICON,"gelthi") + set_trait(TRAIT_PLANT_ICON,"mushroom3") set_trait(TRAIT_ENDURANCE,15) set_trait(TRAIT_MATURATION,6) set_trait(TRAIT_PRODUCTION,6) @@ -1165,11 +1416,12 @@ name = "vale" seed_name = "vale" display_name = "vale bush" + kitchen_tag = "vale" chems = list("paracetamol" = list(1,5),"dexalin" = list(1,2),"nutriment"= list(1,5)) /datum/seed/vale/New() ..() - set_trait(TRAIT_PLANT_ICON,"vale") + set_trait(TRAIT_PLANT_ICON,"flower4") set_trait(TRAIT_ENDURANCE,15) set_trait(TRAIT_MATURATION,8) set_trait(TRAIT_PRODUCTION,10) @@ -1180,11 +1432,12 @@ name = "surik" seed_name = "surik" display_name = "surik vine" + kitchen_tag = "surik" chems = list("impedrezene" = list(1,3),"synaptizine" = list(1,2),"nutriment" = list(1,5)) /datum/seed/surik/New() ..() - set_trait(TRAIT_PLANT_ICON,"surik") + set_trait(TRAIT_PLANT_ICON,"bush6") set_trait(TRAIT_ENDURANCE,18) set_trait(TRAIT_MATURATION,7) set_trait(TRAIT_PRODUCTION,7) diff --git a/code/modules/hydroponics/seed_gene_mut.dm b/code/modules/hydroponics/seed_gene_mut.dm index cdb7048957..09147c72b9 100644 --- a/code/modules/hydroponics/seed_gene_mut.dm +++ b/code/modules/hydroponics/seed_gene_mut.dm @@ -6,24 +6,24 @@ var/datum/seed/S = diverge() //Let's not modify all of the seeds. T.visible_message("\The [S.display_name] quivers!") //Mimicks the normal mutation. G.mutate(S, T) - + return S - + /decl/plantgene var/gene_tag - + /decl/plantgene/biochem gene_tag = GENE_BIOCHEMISTRY - + /decl/plantgene/hardiness gene_tag = GENE_HARDINESS - + /decl/plantgene/environment gene_tag = GENE_ENVIRONMENT /decl/plantgene/metabolism gene_tag = GENE_METABOLISM - + /decl/plantgene/structure gene_tag = GENE_STRUCTURE @@ -38,7 +38,7 @@ /decl/plantgene/atmosphere gene_tag = GENE_ATMOSPHERE - + /decl/plantgene/vigour gene_tag = GENE_VIGOUR @@ -53,7 +53,7 @@ /decl/plantgene/biochem/mutate(var/datum/seed/S) S.set_trait(TRAIT_POTENCY, S.get_trait(TRAIT_POTENCY)+rand(-20,20),200, 0) - + /decl/plantgene/hardiness/mutate(var/datum/seed/S) if(prob(60)) S.set_trait(TRAIT_TOXINS_TOLERANCE, S.get_trait(TRAIT_TOXINS_TOLERANCE)+rand(-2,2),10,0) @@ -63,7 +63,7 @@ S.set_trait(TRAIT_WEED_TOLERANCE, S.get_trait(TRAIT_WEED_TOLERANCE)+rand(-2,2),10,0) if(prob(60)) S.set_trait(TRAIT_ENDURANCE, S.get_trait(TRAIT_ENDURANCE)+rand(-5,5),100,0) - + /decl/plantgene/environment/mutate(var/datum/seed/S) if(prob(60)) S.set_trait(TRAIT_IDEAL_HEAT, S.get_trait(TRAIT_IDEAL_HEAT)+rand(-2,2),10,0) @@ -71,7 +71,7 @@ S.set_trait(TRAIT_IDEAL_LIGHT, S.get_trait(TRAIT_IDEAL_LIGHT)+rand(-2,2),10,0) if(prob(60)) S.set_trait(TRAIT_LIGHT_TOLERANCE, S.get_trait(TRAIT_LIGHT_TOLERANCE)+rand(-5,5),100,0) - + /decl/plantgene/metabolism/mutate(var/datum/seed/S) if(prob(65)) S.set_trait(TRAIT_REQUIRES_NUTRIENTS, S.get_trait(TRAIT_REQUIRES_NUTRIENTS)+rand(-2,2),10,0) @@ -79,7 +79,7 @@ S.set_trait(TRAIT_REQUIRES_WATER, S.get_trait(TRAIT_REQUIRES_WATER)+rand(-2,2),10,0) if(prob(40)) S.set_trait(TRAIT_ALTER_TEMP, S.get_trait(TRAIT_ALTER_TEMP)+rand(-5,5),100,0) - + /decl/plantgene/diet/mutate(var/datum/seed/S) if(prob(60)) S.set_trait(TRAIT_CARNIVOROUS, S.get_trait(TRAIT_CARNIVOROUS)+rand(-1,1),2,0) @@ -102,17 +102,15 @@ T.visible_message("\The [S.display_name]'s glow dims...") if(prob(60)) S.set_trait(TRAIT_PRODUCES_POWER, !S.get_trait(TRAIT_PRODUCES_POWER)) - + /decl/plantgene/atmosphere/mutate(var/datum/seed/S) if(prob(60)) - S.set_trait(TRAIT_TOXINS_TOLERANCE, S.get_trait(TRAIT_TOXINS_TOLERANCE)+rand(-2,2),10,0) + S.set_trait(TRAIT_HEAT_TOLERANCE, S.get_trait(TRAIT_HEAT_TOLERANCE)+rand(-2,2),40,0) if(prob(60)) - S.set_trait(TRAIT_PEST_TOLERANCE, S.get_trait(TRAIT_PEST_TOLERANCE)+rand(-2,2),10,0) + S.set_trait(TRAIT_LOWKPA_TOLERANCE, S.get_trait(TRAIT_LOWKPA_TOLERANCE)+rand(-10,10),100,10) if(prob(60)) - S.set_trait(TRAIT_WEED_TOLERANCE, S.get_trait(TRAIT_WEED_TOLERANCE)+rand(-2,2),10,0) - if(prob(60)) - S.set_trait(TRAIT_ENDURANCE, S.get_trait(TRAIT_ENDURANCE)+rand(-5,5),100,0) - + S.set_trait(TRAIT_HIGHKPA_TOLERANCE, S.get_trait(TRAIT_HIGHKPA_TOLERANCE)+rand(-10,10),500,100) + /decl/plantgene/vigour/mutate(var/datum/seed/S, var/turf/T) if(prob(65)) S.set_trait(TRAIT_PRODUCTION, S.get_trait(TRAIT_PRODUCTION)+rand(-1,1),10,0) @@ -121,7 +119,7 @@ if(prob(55)) S.set_trait(TRAIT_SPREAD, S.get_trait(TRAIT_SPREAD)+rand(-1,1),2,0) T.visible_message("\The [S.display_name] spasms visibly, shifting in the tray.") - + /decl/plantgene/fruit/mutate(var/datum/seed/S) if(prob(65)) S.set_trait(TRAIT_STINGS, !S.get_trait(TRAIT_STINGS)) @@ -129,7 +127,7 @@ S.set_trait(TRAIT_EXPLOSIVE, !S.get_trait(TRAIT_EXPLOSIVE)) if(prob(65)) S.set_trait(TRAIT_JUICY, !S.get_trait(TRAIT_JUICY)) - + /decl/plantgene/special/mutate(var/datum/seed/S) if(prob(65)) S.set_trait(TRAIT_TELEPORTING, !S.get_trait(TRAIT_TELEPORTING)) diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index 4601f23484..2acfa4fb72 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -94,10 +94,16 @@ to_chat(user, "You load [W] into [src].") return +//TFF 3/6/19 - fix infinite frame creation, ported from Cit RP - also allow movement of hydroponic-related machines. if(default_deconstruction_screwdriver(user, W)) return - if(default_deconstruction_crowbar(user, W)) + if(W.is_wrench()) + playsound(src, W.usesound, 100, 1) + to_chat(user, "You [anchored ? "un" : ""]secure \the [src].") + anchored = !anchored return +// if(default_deconstruction_crowbar(user, W)) //No circuit boards to give. +// return if(istype(W,/obj/item/weapon/disk/botany)) if(loaded_disk) to_chat(user, "There is already a data disk loaded.") diff --git a/code/modules/hydroponics/seed_mobs.dm b/code/modules/hydroponics/seed_mobs.dm index 88462acb47..0c9441dbac 100644 --- a/code/modules/hydroponics/seed_mobs.dm +++ b/code/modules/hydroponics/seed_mobs.dm @@ -3,6 +3,9 @@ if(!host || !istype(host)) return + if(apply_color_to_mob) + host.color = traits[TRAIT_PRODUCT_COLOUR] + var/datum/ghosttrap/plant/P = get_ghost_trap("living plant") P.request_player(host, "Someone is harvesting [display_name]. ") diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index f4b8b951a5..047a7d895a 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -283,3 +283,42 @@ GLOBAL_LIST_BOILERPLATE(all_seed_packs, /obj/item/seeds) /obj/item/seeds/thaadra seed_type = "thaadra" + +/obj/item/seeds/celery + seed_type = "celery" + +/obj/item/seeds/rhubarb + seed_type = "rhubarb" + +/obj/item/seeds/wabback + seed_type = "whitewabback" + +/obj/item/seeds/blackwabback + seed_type = "blackwabback" + +/obj/item/seeds/wildwabback + seed_type = "wildwabback" + +/obj/item/seeds/lettuce + seed_type = "lettuce" + +/obj/item/seeds/siflettuce + seed_type = "siflettuce" + +/obj/item/seeds/eggyplant + seed_type = "egg-plant" + +/obj/item/seeds/spineapple + seed_type = "spineapple" + +/obj/item/seeds/durian + seed_type = "durian" + +/obj/item/seeds/vanilla + seed_type = "vanilla" + +/obj/item/seeds/rose + seed_type = "rose" + +/obj/item/seeds/rose/blood + seed_type = "bloodrose" diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 135dc09a4c..be7d253659 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -54,7 +54,7 @@ /obj/item/seeds/ambrosiavulgarisseed = 3, /obj/item/seeds/plastiseed = 3, /obj/item/seeds/kudzuseed = 2, - /obj/item/seeds/nettleseed = 1 + /obj/item/seeds/rose/blood = 1 ), list( /obj/item/seeds/ambrosiavulgarisseed = 3, @@ -69,6 +69,7 @@ /obj/item/seeds/bluetomatoseed = 1 ), list( + /obj/item/seeds/durian = 2, /obj/item/seeds/ambrosiadeusseed = 1, /obj/item/seeds/killertomatoseed = 1 ), @@ -99,17 +100,20 @@ /obj/item/seeds/berryseed = 3, /obj/item/seeds/cabbageseed = 3, /obj/item/seeds/carrotseed = 3, + /obj/item/seeds/celery = 3, /obj/item/seeds/chantermycelium = 3, /obj/item/seeds/cherryseed = 3, /obj/item/seeds/chiliseed = 3, /obj/item/seeds/cocoapodseed = 3, /obj/item/seeds/cornseed = 3, + /obj/item/seeds/durian = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/grapeseed = 3, /obj/item/seeds/grassseed = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/lavenderseed = 3, /obj/item/seeds/lemonseed = 3, + /obj/item/seeds/lettuce = 3, /obj/item/seeds/limeseed = 3, /obj/item/seeds/mtearseed = 2, /obj/item/seeds/orangeseed = 3, @@ -119,14 +123,18 @@ /obj/item/seeds/poppyseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/pumpkinseed = 3, + /obj/item/seeds/rhubarb = 3, /obj/item/seeds/riceseed = 3, + /obj/item/seeds/rose = 3, /obj/item/seeds/soyaseed = 3, + /obj/item/seeds/spineapple = 3, /obj/item/seeds/sugarcaneseed = 3, /obj/item/seeds/sunflowerseed = 3, /obj/item/seeds/shandseed = 2, /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/tomatoseed = 3, /obj/item/seeds/towermycelium = 3, + /obj/item/seeds/vanilla = 3, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, /obj/item/seeds/whitebeetseed = 3 @@ -144,11 +152,13 @@ /obj/item/seeds/berryseed = 3, /obj/item/seeds/cabbageseed = 3, /obj/item/seeds/carrotseed = 3, + /obj/item/seeds/celery = 3, /obj/item/seeds/chantermycelium = 3, /obj/item/seeds/cherryseed = 3, /obj/item/seeds/chiliseed = 3, /obj/item/seeds/cocoapodseed = 3, /obj/item/seeds/cornseed = 3, + /obj/item/seeds/durian = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/glowshroom = 2, @@ -156,6 +166,7 @@ /obj/item/seeds/grassseed = 3, /obj/item/seeds/lavenderseed = 3, /obj/item/seeds/lemonseed = 3, + /obj/item/seeds/lettuce = 3, /obj/item/seeds/libertymycelium = 2, /obj/item/seeds/limeseed = 3, /obj/item/seeds/mtearseed = 2, @@ -168,14 +179,19 @@ /obj/item/seeds/potatoseed = 3, /obj/item/seeds/pumpkinseed = 3, /obj/item/seeds/reishimycelium = 2, + /obj/item/seeds/rhubarb = 3, /obj/item/seeds/riceseed = 3, + /obj/item/seeds/rose = 3, /obj/item/seeds/soyaseed = 3, + /obj/item/seeds/spineapple = 3, /obj/item/seeds/sugarcaneseed = 3, /obj/item/seeds/sunflowerseed = 3, /obj/item/seeds/shandseed = 2, /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/tomatoseed = 3, /obj/item/seeds/towermycelium = 3, + /obj/item/seeds/vanilla = 3, + /obj/item/seeds/wabback = 2, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, /obj/item/seeds/whitebeetseed = 3 diff --git a/code/modules/hydroponics/seed_storage_vr.dm b/code/modules/hydroponics/seed_storage_vr.dm index 6d9d373b78..77a885276c 100644 --- a/code/modules/hydroponics/seed_storage_vr.dm +++ b/code/modules/hydroponics/seed_storage_vr.dm @@ -5,17 +5,20 @@ /obj/item/seeds/berryseed = 3, /obj/item/seeds/cabbageseed = 3, /obj/item/seeds/carrotseed = 3, + /obj/item/seeds/celery = 3, /obj/item/seeds/chantermycelium = 3, /obj/item/seeds/cherryseed = 3, /obj/item/seeds/chiliseed = 3, /obj/item/seeds/cocoapodseed = 3, /obj/item/seeds/cornseed = 3, + /obj/item/seeds/durian = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/grapeseed = 3, /obj/item/seeds/grassseed = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/lavenderseed = 3, /obj/item/seeds/lemonseed = 3, + /obj/item/seeds/lettuce = 3, /obj/item/seeds/limeseed = 3, /obj/item/seeds/mtearseed = 2, /obj/item/seeds/orangeseed = 3, @@ -25,19 +28,24 @@ /obj/item/seeds/poppyseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/pumpkinseed = 3, + /obj/item/seeds/rhubarb = 3, /obj/item/seeds/riceseed = 3, + /obj/item/seeds/rose = 3, /obj/item/seeds/soyaseed = 3, + /obj/item/seeds/spineapple = 3, /obj/item/seeds/sugarcaneseed = 3, /obj/item/seeds/sunflowerseed = 3, /obj/item/seeds/shandseed = 2, /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/tomatoseed = 3, /obj/item/seeds/towermycelium = 3, + /obj/item/seeds/vanilla = 3, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, /obj/item/seeds/whitebeetseed = 3, /obj/item/seeds/shrinkshroom = 3, - /obj/item/seeds/megashroom = 3) + /obj/item/seeds/megashroom = 3, + /obj/item/seeds/wabback = 2) /obj/machinery/seed_storage/xenobotany name = "Xenobotany seed storage" @@ -50,11 +58,13 @@ /obj/item/seeds/berryseed = 3, /obj/item/seeds/cabbageseed = 3, /obj/item/seeds/carrotseed = 3, + /obj/item/seeds/celery = 3, /obj/item/seeds/chantermycelium = 3, /obj/item/seeds/cherryseed = 3, /obj/item/seeds/chiliseed = 3, /obj/item/seeds/cocoapodseed = 3, /obj/item/seeds/cornseed = 3, + /obj/item/seeds/durian = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/glowshroom = 2, @@ -62,6 +72,7 @@ /obj/item/seeds/grassseed = 3, /obj/item/seeds/lavenderseed = 3, /obj/item/seeds/lemonseed = 3, + /obj/item/seeds/lettuce = 3, /obj/item/seeds/libertymycelium = 2, /obj/item/seeds/limeseed = 3, /obj/item/seeds/mtearseed = 2, @@ -74,14 +85,19 @@ /obj/item/seeds/potatoseed = 3, /obj/item/seeds/pumpkinseed = 3, /obj/item/seeds/reishimycelium = 2, + /obj/item/seeds/rhubarb = 3, /obj/item/seeds/riceseed = 3, + /obj/item/seeds/rose = 3, /obj/item/seeds/soyaseed = 3, + /obj/item/seeds/spineapple = 3, /obj/item/seeds/sugarcaneseed = 3, /obj/item/seeds/sunflowerseed = 3, /obj/item/seeds/shandseed = 2, /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/tomatoseed = 3, /obj/item/seeds/towermycelium = 3, + /obj/item/seeds/vanilla = 3, + /obj/item/seeds/wabback = 2, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, /obj/item/seeds/whitebeetseed = 3, diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm index c2c70985c5..cdf4652d5e 100644 --- a/code/modules/integrated_electronics/core/printer.dm +++ b/code/modules/integrated_electronics/core/printer.dm @@ -44,7 +44,7 @@ if(num < 1) to_chat(user, span("warning", "\The [src] is too full to add more metal.")) return - if(stack.use(num)) + if(stack.use(max(1, round(num)))) // We don't want to create stacks that aren't whole numbers to_chat(user, span("notice", "You add [num] sheet\s to \the [src].")) metal += num * metal_per_sheet interact(user) @@ -148,7 +148,7 @@ else var/obj/item/I = build_type cost = initial(I.w_class) - if(!build_type in SScircuit.circuit_fabricator_recipe_list[current_category]) + if(!(build_type in SScircuit.circuit_fabricator_recipe_list[current_category])) return if(!debug) diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index f12db3f76b..4ac81ea4cc 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -14,7 +14,7 @@ can_be_asked_input = 1 inputs = list() outputs = list() - activators = list("on pressed" = IC_PINTYPE_PULSE_IN) + activators = list("on pressed" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -31,7 +31,7 @@ can_be_asked_input = 1 inputs = list() outputs = list("on" = IC_PINTYPE_BOOLEAN) - activators = list("on toggle" = IC_PINTYPE_PULSE_IN) + activators = list("on toggle" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH /obj/item/integrated_circuit/input/toggle_button/ask_for_input(mob/user) // Ditto. @@ -48,7 +48,7 @@ can_be_asked_input = 1 inputs = list() outputs = list("number entered" = IC_PINTYPE_NUMBER) - activators = list("on entered" = IC_PINTYPE_PULSE_IN) + activators = list("on entered" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH power_draw_per_use = 4 @@ -67,7 +67,7 @@ can_be_asked_input = 1 inputs = list() outputs = list("string entered" = IC_PINTYPE_STRING) - activators = list("on entered" = IC_PINTYPE_PULSE_IN) + activators = list("on entered" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH power_draw_per_use = 4 @@ -86,7 +86,7 @@ can_be_asked_input = 1 inputs = list() outputs = list("color entered" = IC_PINTYPE_COLOR) - activators = list("on entered" = IC_PINTYPE_PULSE_IN) + activators = list("on entered" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH power_draw_per_use = 4 @@ -308,11 +308,12 @@ var/list/valid_things = list() if(isweakref(I.data)) var/atom/A = I.data.resolve() - var/desired_type = A.type - if(desired_type) - for(var/atom/thing in nearby_things) - if(thing.type == desired_type) - valid_things.Add(thing) + if(A) + var/desired_type = A.type + if(desired_type) + for(var/atom/thing in nearby_things) + if(thing.type == desired_type) + valid_things.Add(thing) else if(istext(I.data)) var/DT = I.data for(var/atom/thing in nearby_things) @@ -540,7 +541,57 @@ if(translated) activate_pin(2) +/obj/item/integrated_circuit/input/microphone/sign + name = "sign-language translator" + desc = "Useful for spying on people or for sign activated machines." + extended_desc = "This will automatically translate galactic standard sign language it sees to Galactic Common. \ + The first activation pin is always pulsed when the circuit sees someone speak sign, while the second one \ + is only triggered if it sees someone speaking a language other than sign language, which it will attempt to \ + lip-read." + icon_state = "video_camera" + complexity = 12 + inputs = list() + outputs = list( + "speaker" = IC_PINTYPE_STRING, + "message" = IC_PINTYPE_STRING + ) + activators = list("on message received" = IC_PINTYPE_PULSE_OUT, "on translation" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 30 + var/list/my_langs = list() + var/list/readable_langs = list( + LANGUAGE_GALCOM, + LANGUAGE_SOL_COMMON, + LANGUAGE_TRADEBAND, + LANGUAGE_GUTTER, + LANGUAGE_TERMINUS + ) + +/obj/item/integrated_circuit/input/microphone/sign/Initialize() + ..() + for(var/lang in readable_langs) + var/datum/language/newlang = all_languages[lang] + my_langs |= newlang + +/obj/item/integrated_circuit/input/microphone/sign/hear_talk(mob/living/M, msg, var/verb="says", datum/language/speaking=null) + var/translated = FALSE + if(M && msg) + if(speaking) + if(!((speaking.flags & NONVERBAL) || (speaking.flags & SIGNLANG))) + translated = TRUE + msg = speaking.scramble(msg, my_langs) + set_pin_data(IC_OUTPUT, 1, M.GetVoice()) + set_pin_data(IC_OUTPUT, 2, msg) + + push_data() + activate_pin(1) + if(translated) + activate_pin(2) + +/obj/item/integrated_circuit/input/microphone/sign/hear_signlang(text, verb, datum/language/speaking, mob/M as mob) + hear_talk(M, text, verb, speaking) + return /obj/item/integrated_circuit/input/sensor name = "sensor" diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index 1e7ad6c31a..3ddc3458d7 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -136,6 +136,33 @@ var/obj/O = assembly ? loc : assembly audible_message("\icon[O] \The [O.name] states, \"[text]\"") +/obj/item/integrated_circuit/output/text_to_speech/advanced + name = "advanced text-to-speech circuit" + desc = "A miniature speaker is attached to this component. It is able to transpose any valid text to speech, matching a scanned target's voice." + complexity = 15 + cooldown_per_use = 6 SECONDS + inputs = list("text" = IC_PINTYPE_STRING, "mimic target" = IC_PINTYPE_REF) + power_draw_per_use = 100 + + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_ILLEGAL = 1) + + var/mob/living/voice/my_voice + +/obj/item/integrated_circuit/output/text_to_speech/advanced/Initialize() + ..() + my_voice = new (src) + my_voice.name = "TTS Circuit" + +/obj/item/integrated_circuit/output/text_to_speech/advanced/do_work() + text = get_pin_data(IC_INPUT, 1) + var/mob/living/target_mob = get_pin_data(IC_INPUT, 2) + my_voice.transfer_identity(target_mob) + if(!isnull(text) && !isnull(my_voice) && !isnull(my_voice.name)) + my_voice.forceMove(get_turf(src)) + my_voice.say("[text]") + my_voice.forceMove(src) + /obj/item/integrated_circuit/output/sound name = "speaker circuit" desc = "A miniature speaker is attached to this component." @@ -244,7 +271,7 @@ activators = list() spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH power_draw_idle = 5 // Raises to 80 when on. - var/obj/machinery/camera/network/research/camera + var/obj/machinery/camera/network/circuits/camera /obj/item/integrated_circuit/output/video_camera/New() ..() diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 734a2bcfa5..3c7ebec377 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -141,6 +141,7 @@ icon_state ="book" throw_speed = 1 throw_range = 5 + flags = NOCONDUCT w_class = ITEMSIZE_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) attack_verb = list("bashed", "whacked", "educated") var/dat // Actual page content diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 088c53e521..30230a69e0 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -99,9 +99,9 @@ /datum/lighting_corner/proc/update_overlays() // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually. - var/lum_r = src.lum_r - var/lum_g = src.lum_g - var/lum_b = src.lum_b + var/lum_r = src.lum_r > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_r) : src.lum_r + var/lum_g = src.lum_g > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_g) : src.lum_g + var/lum_b = src.lum_b > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_b) : src.lum_b var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1. . = 1 // factor if (mx > 1) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index d878d8c4b6..451b8273c8 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -189,7 +189,7 @@ ); // This is the define used to calculate falloff. -#define LUM_FALLOFF(C, T)(1 - CLAMP01(sqrt((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range))) +#define LUM_FALLOFF(C, T)(1 - CLAMP01(((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) ** 0.6 / max(1, light_range))) /datum/light_source/proc/apply_lum() var/static/update_gen = 1 diff --git a/code/modules/lore_codex/news_data/main.dm b/code/modules/lore_codex/news_data/main.dm index ee41280d6f..277c38e175 100644 --- a/code/modules/lore_codex/news_data/main.dm +++ b/code/modules/lore_codex/news_data/main.dm @@ -4,6 +4,40 @@ region. Each is labeled by date of publication and title. This list is self-updating, and from time to time the publisher will push new \ articles. You are encouraged to check back frequently." children = list( + /datum/lore/codex/page/article66, + /datum/lore/codex/page/article65, + /datum/lore/codex/page/article64, + /datum/lore/codex/page/article63, + /datum/lore/codex/page/article62, + /datum/lore/codex/page/article61, + /datum/lore/codex/page/article60, + /datum/lore/codex/page/article59, + /datum/lore/codex/page/article58, + /datum/lore/codex/page/article57, + /datum/lore/codex/page/article56, + /datum/lore/codex/page/article55, + /datum/lore/codex/page/article54, + /datum/lore/codex/page/article53, + /datum/lore/codex/page/article52, + /datum/lore/codex/page/article51, + /datum/lore/codex/page/article50, + /datum/lore/codex/page/article49, + /datum/lore/codex/page/article48, + /datum/lore/codex/page/article47, + /datum/lore/codex/page/article46, + /datum/lore/codex/page/article45, + /datum/lore/codex/page/article44, + /datum/lore/codex/page/article43, + /datum/lore/codex/page/article42, + /datum/lore/codex/page/article41, + /datum/lore/codex/page/article40, + /datum/lore/codex/page/article39, + /datum/lore/codex/page/keldowinterview, + /datum/lore/codex/category/article38, + /datum/lore/codex/page/article37, + /datum/lore/codex/page/article36, + /datum/lore/codex/page/article35, + /datum/lore/codex/page/article34, /datum/lore/codex/page/article33, /datum/lore/codex/page/article32, /datum/lore/codex/page/bjornretirement, @@ -401,3 +435,350 @@ Vani Jee is running on a platform of free access to education, Sivian self-determination, and isolationist foreign policy. She has refused to make any strong statements regarding hot-button issues such as the Five Points.\

      \ She intends to resume her scheduled tour after a three day break." + +/datum/lore/codex/page/article34 + name = "02/05/63 - Angessa Martei to Take Control of Eponymous Colony" + data = "Coming out of retirement and displacing the nameless Exalt of the Starlit Path, religious demagogue Angessa Martei has returned to the throne of the colony that bears her name. \ +

      \ + 'I had retired because of senescence brought on by my old age, high-stress lifestyle, and multiple resurrective clonings. As you may know, I recently had a procedure that renders these difficulties obsolete. Hereafter I will continue to control the automated facilities of the Pearl and claim responsibility for the collective action of my followers. Those who oppose my decision may oppose all they want. Feelings do not move mountains. I do. We shall seize the stars in our own hands. May you become who you wish to be, and grind all obstacles to dust, as I have done.'\ +

      \ + Purportedly, this address was met with a standing ovation from the population of Angessa's Pearl. In a separate dispatch, Martei stated her intention to tour SolGov as a foreign dignitary protected by the Respect for Diplomats Act." + +/datum/lore/codex/page/article35 + name = "02/07/63 - Vir Gubernatorial Candidate Barred from Breakfast TV" + data = "Infamously hot-headed Shadow Coalition candidate Phaedrus has reportedly been blacklisted from future appearances on morning television by several major networks. The ban comes after an advertised chat segment between Phaedrus and hosts of the West Sif Wakeup breakfast programme had to be pulled from broadcast after the candidate 'Flew into a expletive-laden mercurial rant' at the expense of rival candidate Mehmet Sao of the Icarus Front.\ +

      \ + Recordings of the outburst quickly made their way onto social media, sparking outrage from opponents and network executives alike, prompting Occulum Media to issue a rare blacklist from major media outlets, restricting Phaedrus to 'appearances on alternative news sources' owned by the company.\ +

      \ + Phaedrus, a long-time Vir Mercurial Progress Party member running for a major party for the first time, is said to have taken issue with candidate Sao's 'Blatant anti-synthetic' policies, though he did not use the word 'policies'." + +/datum/lore/codex/page/article36 + name = "02/09/63 - SEO Candidate Embarks on Wilderness Tour" + data = "In an effort to stir up support for his promotion of natural resource extraction industries, Sol Economic Organization candidate Mason Keldow has embarked on an unorthodox tour of resource-rich sites across central Sif. Keldow has described the tour as an 'Old fashioned expedition', invoking images of hardy prospectors of centuries past, and intends to make the journey entirely by ground with only a small party of 'Adventurous outdoorspeople' to support his trek.\ +

      \ + Critics of the plan have pointed out that the earliest surveys of Sif were largely performed by aerial drones, and the idea of ground-based survey teams is 'Frankly anachronistic'. Rival Shadow Coalition candidate Selma Jorg - a staunch planetary environmentalist - has described the tour as 'Irresponsible and insane'.\ +

      \ + The candidate intends to visit both unexploited sites and current corporate extraction facilities in order to 'Better understand the folks helping dig out Sif's hidden wealth' over the coming two weeks." + +/datum/lore/codex/page/article37 + name = "02/09/63 - Zaddat Colony 'Bright' To Enter Vir" + data = "After several months of talks with Nanotrasen and other corporations in the system, the Colony Bright is to begin orbiting Sif and hardsuited Zaddat are to enter the Virite workforce. Executives in Nanotrasen Vir cite the reduction of their drone-automated and positronic workforce as a result of the Gray Hour as cause for them to reverse their previous decision against allowing the migrants into the system. Icarus officials within VGA are concerned that, if other Colonies are to follow the Bright, the native industry of Sif may be disrupted or suborned by Zaddat and Hegemony interests, and have made it clear that the Bright's presence in the system is highly conditional." + +/datum/lore/codex/category/article38 + name = "02/11/63 - Mason Keldow in Ullran Expanse Close Call" + data = "Sol Economic Organization candidate Mason Keldow was rushed to nearby corporate medical facilities after a death-defying encounter with local wildlife in the Ullran Expanse this morning. The candidate had been planning to visit the nearby NanoTrasen mining facilities as part of his much publicized 'Wilderness Tour' when he and a local guide were set upon by the notoriously savage Sivian Savik. The animal was killed in the encounter, but not before Mr. Keldow suffered life-threatening injuries and had to be recovered by crew from the NLS Southern Cross, the closest facility on hand.\ +

      \ + Following emergency surgery, Keldow was happy to provide news sources with a 'Good-natured' interview, in which he highlighted the dangers faced by rural workers on Sif and his plans to tackle them, as well as slamming rival Shadow Coalition candidate Selma Jorg for lacking tangible plans for the future.\ +

      \ + Candidate Keldow is reported to have made a miraculous recovery, and is 'in good spirits'. Aides state that he is unlikely to suffer any long term effects from the injuries, in part thanks for the skilful work of NanoTrasen's Dr. Fuerte.\ +

      \ + A full transcript of the interview follows:" + children = list( + /datum/lore/codex/page/keldowinterview + ) + +/datum/lore/codex/page/keldowinterview + name = "Mason Keldow Interview Transcript" + data = "Blip asks, 'Subject Keldow. Pleasantries first. How do you feel after your ordeal on the planet's surface?'\ +

      \ + Mason Keldow says, 'Ah that? You know I'd love to downplay it and pretend that it was just a walk in the park... But NT's medical staff probably will tell you otherwise, so there's no reason to hide it; things went pretty far south.'\ +

      \ + Mason Keldow says, 'But that's how it goes, working on the surface of Sif isn't pleasant at times.'\ +

      \ + Blip asks, 'Candidate Keldow, you have placed yourself quite firmly in the boots of the local TSC's explorer contingents today. Do you feel you will be attempting to live the life of any other labour intensive roles in the near future?'\ +

      \ + Mason Keldow says, 'Blip, Let me tell you I do try to get a taste for a lot of the work done by these people... But admittedly this isn't the job I do every day. These are hard working fellows who do there damnedest day in and day out... I could try spending a week just working the mines, But-'\ +

      \ + Mason Keldow says, 'As I was saying.. Many of the folks, Miners, Explorers, The work in and around the Ullran Expanse. They work in the mountains, Out in the fields.. It's a dangerous place and frankly its not a place average people wanna go too.'\ +

      \ + Mason Keldow says, 'They told me on the way here. 'Keldow you're an idiot''\ +

      \ + Mason Keldow says, 'Hell, Even Basman over here was wondering why I didn't ask for a detail.'\ +

      \ + Mason Keldow says, 'So it's not a safe route... But when's the last time any of the other candidates actually came down to these outer stretches and tried earning their sweat.'\ +

      \ + Blip asks, 'You indeed seem to be attempting to gain a unique, and firm understanding of the daily struggles of the working populace. How do you intend to translate this newfound knowledge into policy and direction if you take the Governorship in Vir?'\ +

      \ + Mason Keldow says, 'You see, getting a grasp of the struggle is only step one, I paint myself as an every-man but that doesn't mean core issues aren't the problem either; Vir's economics, The small pay that sometimes offered. There is a lot to be tapped into.'\ +

      \ + Mason Keldow says, 'Let's take for instance the spiders I've been hearing about.'\ +

      \ + Mason Keldow says, 'People working in orbit say 'Don't go to the surface, Spiders are down there.''\ +

      \ + Mason Keldow says, 'And apparently there was a big ol' purple one sitting right by a camp we had set up. A giant mother who'd - if I hadn't met that lovely mass of fur and ice instead - would have probably said its “hello” in the worst possible way.'\ +

      \ + Mason Keldow says, 'They are a species that prevents anyone from actually working or otherwise making use of all that land. If I were in office, I'd make an effort to clear out the dangerous species that surround the outer regions of Sif – relocate them if possible - and use that territory for something productive.'\ +

      \ + Mason Keldow says, 'New forms of Transit, new buildings, new jobs.'\ +

      \ + Blip asks, 'Such implementation of infrastructure and security is not a cheap measure. How do you intend to find funds for such an endeavour?'\ +

      \ + Mason Keldow says, 'Now obviously Vir is in a very interesting position, But thankfully it's in a wonderful position where business and partnerships are more than happy to come in and assist. The bottom line would make sure the average Taxpayer doesn't feel a dent, only the dividends.'\ +

      \ + Mason Keldow says, 'If we break this down into economic plans, using new yet relatively safe tools being put out by Hepheastus, you could safely clear swaths of territory in the Expanse.'\ +

      \ + Blip says, 'Thank you for your insight into your economic policies and plans. As a final question;'\ +

      \ + Blip asks, 'A puff-piece question. Do you have anything positive to say about your rivals in the political race?'\ +

      \ + Mason Keldow says, 'Ah yes, yes well... As for any candidate they need to show they're worth. Not simply as a politician but as a person who believes what they will do for for the betterment of Sif, And Vir as a whole.'\ +

      \ + Mason Keldow says, 'Let's take a look Ms. Jorg.'\ +

      \ + Mason Keldow says, 'She has LONG called me a Corporate sell-out, Saying I would poison the planet and other awful mudslinging.'\ +

      \ + Mason Keldow says, 'She loves to claim she's here for the better of the misrepresented.'\ +

      \ + Mason Keldow says, 'But when is the last time she's talked to a Tajaran and told them how they will help put food on the table, and money into their pockets.'\ +

      \ + Mason Keldow says, 'When has she came and told the Unathi Exile, Your worth more than what the Hegemony is trying to convince you you're worth.'\ +

      \ + Mason Keldow says, 'There's blood in the grass out there showing what I'm willing to do to make Sif and Vir a better more prosperous system. I wanna see what the other Candidates will do.'\ +

      \ + Blip pings!\ +

      \ + Blip says, 'Thank you for your time, Candidate Keldow. Unit looks forward to seeing where this race ends, and wishes Candidate the best of luck in his endeavours.'\ +

      \ + Mason Keldow says, 'It's been a pleasure Blip.'" + +/datum/lore/codex/page/article39 + name = "02/12/63 - VirGov Launches Election Website" + data = "The Vir Governmental Authority has launched this year's election information exonet site, unusually several months after campaigning began. The government election agency states that the delay was caused by an usually long process of finalizing candidates this cycle, and did not want to confuse voters with incorrect or outdated information.\ +

      \ + The newly updated site includes information on candidates and political parties, and is planned to include information on local voting rights at a future date. It can be found at:\ +

      \ + your-choice-vir.virgov.xo.vr\ +

      \ + (( https://your-choice-vir.weebly.com/ ))" + +/datum/lore/codex/page/article40 + name = "02/14/63 - Ultimatum Unmet: War With Almach!" + data = "The Solar Confederate Government has resumed a state of war against the secessionist Almach Association, after 4 months of tense ceasefire. The re-declaration comes after the Association failed to meet requirements set forth by the Colonial Assembly last November, which called for the cessation and destruction of all research that did not meet standards established by the Five Points of Human Sanctity.\ +

      \ + The past few weeks have been marked by an increasing buildup of military forces on the Almach border as it became apparent that Almach had no intention of meeting Sol's demands. At 9am this morning, the deadline was met and initial reports from the frontline suggest relatively little action besides the destruction of pre-existing Almach scout drones that had been placed on the border several months prior. The exact plans of the fleet going forward have not been made public, but civilian traffic to and from the Saint Columbia system has been entirely suspended.\ +

      \ + How this development will influence the coming Vir election remains to be seen, though SEO candidate Mason Keldow has reportedly ended his planetary tour 10 days earlier than planned due to the tense political situation." + +/datum/lore/codex/page/article41 + name = "02/22/63 - Militia Retreats: First Solar Victory" + data = "After a week of tense stand-offs and increasingly frequent skirmishes, the Association Militia has begun moving from their position around Saint Columbia further into the Almach Rim. The inciting incident for this shift seems to have been the first use of the MJOLNIR system, which instantly destroyed a large Almachi warship from half a system away. This demonstration was met with a standing ovation from much of Iserlohn, and Militia forces disengaged almost immediately. Admiral McMullen is unwilling to elaborate on pursuit or invasion plans at this moment, but 'hope(s) the MJOLNIR will continue to be a valuable asset for national defense.'" + +/datum/lore/codex/page/article42 + name = "03/04/63 - Savik Slams Local Chat Host" + data = "Television sweetheart Sally, host of Chat With Sally has come under harsh criticism from independent Vir gubernatorial candidate Yole Savik after his 'humiliating' appearance on the show yesterday morning alongside Sol Economic Organization candidate Calvert Moravec. Savik - who is campaigning for Vir independence from both the SCG and corporate interests - alleges that the show, which has run for 13 years on select networks, is 'little more than a propaganda piece for high powered executives.' and that his appearance had been part of a 'smear campaign' against non-SEO candidates.\ +

      \ + NanoTrasen, who have openly sponsored Sally since her inception deny these accusations. Jan Bhatt of the NT marketing division stated 'Chat With Sally has always been intended as light morning entertainment, and Sally a personality we can all relate to. The fact that Mr. Savik was unable to have a sense of humour about the whole thing and took the show as an opportunity to bloviate about dry politics is not an indictment of Sally, nor the corporation but rather a simple misunderstanding of the purpose of the segment. We had hoped Mr. Savik's appearance would help dismiss any claims of political bias in our programming and hope to host more civil candidates in the future.'\ +

      \ + Catch Chat With Sally weekly at 5am SST." + +/datum/lore/codex/page/article43 + name = "03/06/63 - Dark Triangle Goes Dark!" + data = "As of 0352 this morning, New Reykjavik time, SolGov officials confirmed that all communications coming from the so-called 'Dark Triangle' had ceased. The Dark Triangle is a disputed region of space home to the independent world Natuna, which has maintained a more or less neutral relationship with SolGov for a little more than a decade.\ +

      \ + The announcement gives no cause for the communications blackout, though sources with inside knowledge claim that it was completely unforeseen. Some speculate that Natuna, who became an 'observer nation' of the Almach Association last November, is making a political statement, though experts in the telecommunications field are uncertain as to how such a complete blackout is possible.\ +

      \ + Dr. Ina Lai from the Kara Interstellar Observatory states, 'we've never seen anything like this outside of the (Skrellian) Far Kingdoms, and frankly we're at a loss for who might be responsible,' adding that 'the tachyon signatures from the whole region are masked, even those from stellar phenomena or normal bluespace travel.' Independent explorers from the FTU have set out to the region in an attempt to re-establish communication with Natuna and smaller human settlements nearby." + +/datum/lore/codex/page/article44 + name = "03/08/63 - Dark Triangle Overrun By Hegemony" + data = "FTU explorers have re-established exonet communications with the ruling bodies of Natuna and the Dark Triangle. Unfortunately, they also discovered that Natuna's previously autonomous townships have been subsumed into the Moghes Hegemony. \ +

      \ + Bluespace-lensed telescopes throughout SolGov, including the Vir-based Kara Interstellar Observatory, can once again pick up on tachyon signatures in the region. Traffic has been described as 'lower than usual' and no significant fleet assets are believed to be present in the region, though fixed-placement Hegemony installations now litter the Triangle's major star systems. Systems with significant Hegemony presence include Natuna and Ukupanipo, home to the primitive Uehshad species. Some have speculated that the presence of the Uehshad is the reason for the unexpected Hegemony takeover, though Icarus Front General Secretary Mackenzie West was quick to decry the move as 'an obvious imperial land-grab.'\ +

      \ + Hegemony diplomats on Luna and elsewhere have been quick to justify their actions. 'The Dark Triangle has been home to various criminal elements for several centuries,' says Aksere Eko Azaris, a major Hegemony diplomat since the early post-war years. 'Neither the Skrell nor the Solar Confederacy have proven any willingness to bring stability to the region. Local governments such as Natuna have actively encouraged piracy, smuggling, and other acts of banditry, instead of making any moves to legitimize themselves. This instability proved detrimental to the health and wellbeing of all living within striking distance of the pirates of Ue-Orsi, and it was deemed unfortunate, but necessary, that we step in and provide the guiding hand by which this region might be brought back into the fold of civilization, as is our duty as sapients.'\ +

      \ + In a statement closer to home,Commander Iheraer Saelho of the Zaddat Escort Fleet has assured VirGov that 'we only took action to protect the innocents of the Dark Triangle and of neighboring systems'.He asserts that Hegemony rule will ultimately benefit all races of people within the Triangle, and promises that, 'the people of Vir, of Oasis, of the Golden Crescent writ large, have nothing to fear from our clients the Zaddat, or from the Hegemony vessels assigned to their protection.'\ +

      \ + Only time will tell if Saelho's promised peace and stability will manifest in truth. \ +

      \ + This newfound militancy of the Hegemony is likely to become a major campaign issue in the upcoming Vir elections, alongside involvement in the war with the Almach Association and traditionally Virite issues of corporate authority, minority-friendly infrastructure, and taxation." + +/datum/lore/codex/page/article45 + name = "03/12/63 - Ue-Orsi Escapes Hegemony Triangle" + data = "The lawless 'Ue-Orsi' flotilla, home to hundreds of thousands of outcast Skrellian pirates, has departed from the Hegemony-controlled Dark Triangle after what appears to be a brief battle with several Unathi warships. The action damaged several important Orsian ships, including their massive and venerable solar array 'Suqot-Thoo'm', a development which is likely to increase the pirates' aggression in the coming months as they search for additional power sources. It is unclear exactly where the flotilla has fled, though best guesses indicate that they are presently in Skrell space, likely near the lightly-patrolled Xe'Teq system. The Moghes Hegemony is in negotiations with several Skrellian states to arrange for military action against their escaped subjects, but little headway has been made thus far.\ +

      \ + This revelation has added more fuel to already heated Assembly arguments about SolGov response to the Unathi takeover. 'This is a prelude to invasion, nothing more and nothing less,' says New Seoul Representative Collin So-Yung, a noted Iconoclast. 'We must make it absolutely clear to the Hegemony that this is a threat we will not bow to, even in our present state of internal weakness. I suggest we pursue a fair peace with the Association, one where we can keep them as allies against this sort of encroachment instead of shattering our fleets during such a pivotal moment.'\ +

      \ + Others took a more nuanced approach, including VGA Governor Bjorn Arielsson. 'What we have here is our punishment for how badly we've treated the people of the Triangle. I don't really see why we should have let the tired old racism of some Qerr-Katish oligarchs stop us from offering aid to their tired and huddled masses, such as it is. And because we have had a full century of ignoring their plight, they were defenceless to resist the Hegemony. I say we fling the doors open, let (the Orsians) settle some rock here, and show the unaligned powers of the galaxy that the Hegemony's way isn't the only way.'\ +

      \ + Even more conciliatory was Speaker ISA-5, who Arielsson blames for mistreatment of the Ue-Katish. 'Our policy has always been that our defence budget cannot adequately defend the Dark Triangle from internal piracy, that Ue-Orsi is a criminal organization using their refugee status as a shield, and that we cannot lift the blockade of Natuna until they stop hosting these criminals and transition to a more sustainable economy. If Moghes has the power and the inclination to administer the Triangle, I see no reason why this state of affairs isn't better than the alternatives.'" + +/datum/lore/codex/page/article46 + name = "03/26/63 - Almach Routed from Saint Columbia" + data = "The Saint Columbia system has been declared free of Association forces following a renewed SCG offensive in the region. Admiral McMullen of the Saint Columbia garrison - who has reportedly reclaimed his post at the system's naval base despite the facility suffering moderate damage in this week's fighting - says that the last secessionist vessels were driven from the system just over 24 hours ago, and remaining pockets of resistance have been quick to lay down their arms. At least 20 enemy vessels - mostly converted civilian ships - have been confirmed disabled or destroyed in-system, thanks in no small part to the deployment of the state-of-the-art MJOLNIR weapons system.\ +

      \ + This more aggressive approach to the Almach front comes on the tail of aggressive Hegemony deployments in the Dark Triangle, which SolGov has conceded was 'Immediately threatening, but after some deliberation, has brought some form of policing to a lawless region.'.\ +

      \ + Despite assurances, this recent action would appear to many to be an effort to quash the Association threat and return fleet forces to the now extended Hegemony border, and some critics of the Almach War have called for a second ceasefire 'In order to focus on the real threat to mankind.'" + +/datum/lore/codex/page/article47 + name = "04/28/63 - Representative Hainirsdottir Reaffirms Pro-Vey Medical Manifesto" + data = "Incumbent Vir Representative Lusia Hainirsdottir has restated her dedication to advanced medical research at a public appearance at a Vey Medical facility in downtown New Reykjavik. Vey Medical has come under some criticism locally in the past year due to its 'accelerated' sapient trials, which Representative Hainirsdottir has strongly endorsed in her current term of office.\ +

      \ + In her statement to staff at the New Reykjavik facility, Lusia promised that under her governorship, the company would not be reprimanded for the deadly Holburn's Disease outbreak in rural Sif this past June which claimed fifteen lives, and in which Vey-Med's involvement was only confirmed this week - as the outbreak 'directly lead' to the development of a new life-saving inoculation which has seen success galaxy-wide.\ +

      \ + Hainirsdottir has long advocated for the promotion of scientific achievements that have taken place in, and undertaken by the people of Vir." + +/datum/lore/codex/page/article48 + name = "05/06/63 - Isak Spar Withdraws from Vir Election" + data = "Independent gubernatorial candidate Isak Spar has withdrawn his name from the running following a 'Public Relations disaster' aboard an orbital NanoTrasen logistics facility. According to witnesses, Spar acted belligerently towards staff members and engaged in vandalism and assault with a deadly weapon during his scheduled visit to the station, which the candidate had opted to undertake alone due to the temporary illness of his campaign manager.\ +

      \ + In a statement just hours after the alleged incident, Isak announced that he would no longer be pursuing Vir Governorship, due to 'The unexpected stresses of a political career.' before plugging his upcoming album, C*** End Savage Turbo Death A** Destruction. The NanoTrasen corporation has decided not to press charges due to Mr. Spar 'Suffering the consequences on a far more significant level than a mere fine.' but will not be inviting Spar back for future visitation.\ +

      \ + Spar's label, Skull Wreck Music has declined to comment at this time but has agreed to pay damages to the victims on behalf of the self-described 'post-pseudo electro-death superstar'." + +/datum/lore/codex/page/article49 + name = "05/15/63 - Solar Fleet Launches Offensive Against Almach" + data = "The first vessels of an SCG Fleet invasion force arrived in the Relan system this morning after a month-long intelligence operation to establish the Almach Association's most vulnerable positions, according to an announcement by Admiral McMullen just hours ago. Relan, which has long been fragmented between the neutral Republic of Taron and the once insurrectory Free Relan Federation - who now control the majority of the system and declared allegiance with Almach early in the crisis - is expected to fall to Confederate forces within 'a matter of weeks' due to its fractious political situation, and relative insignificance to Almach interests.\ +

      \ + According to McMullen, the system's most populous habitat, the Carter Interstellar Spaceport is already under blockade and local resistance has thus far been minimal. The capture of Relan is expected to provide our forces with a major foothold in Almach territory and further advances are expected to be 'trivial', bypassing the Association's defensive positions in Angessa's Pearl.\ +

      \ + The offensive comes weeks after sizable portions of the Fleet were publicly withdrawn from the frontline in order to reinforce the extended border with the Hegemony following their annexation of the Dark Triangle, and is a clear sign that - despite reduced numbers - Fleet command remains confident of Solar victory against the Mercurial rogue state." + +/datum/lore/codex/page/article50 + name = "05/19/63 - 'Drone Operated' Shelfican Ships Storm Sol Siege" + data = "Blockading Solar vessels in the Relan system came under fire today from automated craft originating from the Shelf fleet. The flotilla of drones, described by one survivor as a 'swarm', launched electromagnetic pulse and so-called 'hatch buster' precision missiles against three SCG Defense vessels, inflicting systems damage, and casualties 'in the hundreds' with at least eight service people already confirmed killed in action. The drones are reported to have withdrawn after 'only a few minutes of protracted fire from both sides.'\ +

      \ + The Shelf telops fleet, which was spotted by Fleet forces but not identified as an immediate threat, entered the system early this morning and were understood to be acting as observers to the ongoing battle for the Relan system due to Shelf's official stance of non-aggression -- despite aligning itself with the Association. Shelf has reportedly been unable to be reached for comment on their actions, and their alleged neutrality in matters of war has been seriously called into question by many in the Colonial Assembly.\ +

      \ + The SCG-D Krishna and SCG-D Mogwai of the SCG fleet, and the assisting Oasis logistical vessel, the OG-L Cloud Nine have been withdrawn to an unspecified location for immediate medical assistance and repairs." + +/datum/lore/codex/page/article51 + name = "05/20/63 - Fleet Withdraws - Sol On The Back Foot?" + data = "The Solar Colonial Assembly has confirmed that the Solar Fleet has withdrawn from the contested Relan system due to 'unexpected resistance' from Shelf tele-operated forces. This comes less than 24 hours after three Solar vessels were seriously damaged in an 'ambush' by a large number of Almach-aligned military drones. According to the Fleet, they were unprepared for any significant ship-to-ship combat in the system and will be consolidating their forces. 'This is not a defeat', according to Captain Silvain Astier of the SCG-R Hanoi, speaking unofficially to Occulum News sources 'This is merely a tactical withdrawal in order to reconvene and reassess our plans to restore order to the Almach Rim.'\ +

      \ + A spokesperson for Shelf was quick to contact Fleet forces following the withdrawal with a formal apology for yesterday's incident, describing the previous day's attack as 'A terrible mistake.', blaming 'a miscommunication between our people in telops and the trigger-happy robots', though the veracity of their claims cannot be confirmed." + +/datum/lore/codex/page/article52 + name = "05/21/63 - NanoTrasen Station to Host Major Election Debate" + data = "As the Vir Gubernatorial elections approach, and with several high-profile debates lined up between the leading candidates in the polls, the NanoTrasen corporation is set to host its very own televised event live from one of its major logistical stations in Vir. The NLS Southern Cross, primarily a traffic control outpost managing shipping in Sif orbit, has been selected by the company to host the debate - funded in full by the corporation - due to a series of minor political scandals that have taken place on the platform, and the suitability of unused space onboard.\ +

      \ + Early in the election cycle, NanoTrasen came under fire for its alleged 'manhandling' of Icarus Front candidate Vani Jee, and the confiscation of to-be-televised recordings taken by a party drone. While the company apologised for the incident shortly thereafter, the corporation hopes to mend ties with the potential future representatives by showing that they are capable of hosting civil discourse. More recently, the NLS Southern Cross played host to the 'breakdown' of disgraced former candidate Isak Spar - an episode which was not addressed in NanoTrasen's official statement on the planned debate event." + +/datum/lore/codex/page/article53 + name = "05/26/63 - SEO Candidate Advocates Murder On Live TV!" + data = "Sol Economic Organization candidate Freya Singh has been caught live on camera admitting that she would like to throw an innocent individual out of an airlock for a minor slight. During this afternoon's debate hosted aboard the NLS Southern Cross. Singh is quoted as having said that the event was 'the silliest concept for a debate I've encountered yet, and whoever came up with it should get a promotion, and then be fired out of an airlock.', a clear incitement of violence against Oculum Broadcast staff.\ +

      \ + Magnus Dugal, 48 works for the Oculum Broadcast corporation and is credited with creating the concept for today's debate, the highest rated for this cycle so far. The father of three, who enjoys hoverboarding in his free time, says that he feels 'Threatened' by Singh's comments, and hopes that she will, 'at the bare minimum', issue an official apology to the company and himself.\ +

      \ + Candidate Freya Singh, a career investment banker, spent much of today's debate advocating for reduced safety regulations and the apparent overturning of the Five Points, raising eyebrows across the system. Singh's office claims that her statements were 'a joke', but we do not feel that this is a laughing matter.\ +

      \ + In related news, Shadow Coalition candidate Phaedrus remains under a profanity filter 'house arrest' for the remainder of the election." + +/datum/lore/codex/page/article54 + name = "06/28/63 - Vir Finalizes Dates for Election Voting" + data = "The Vir Governmental Authority has confirmed that voting for Vir's governorship and Colonial Assembly seats will take place on the 29th and 30th of June, with an additional voting period set for Wednesday the 3rd of July to allow for out-of-system and full-time weekend employees to cast their votes. No exit poll information will be released until the final votes have been cast, and final results are expected to be announced within another week.\ +

      \ + According to a Oculum poll, Lusia Hainirsdottir is expected to comfortably take a seat, though the certainty of her governor position is not hard set. Candidates Sao, Singh and Jorg are trailing not far behind, but will all have to make good showings this weekend if they hope for electoral success. In an unexpected surge among minority species, the Shadow Coalition's Tajaran candidate Kurah Zarshir is leading the polls in certain outlying and orbital communities.\ +

      \ + Not sure how to vote, if you can vote, or who to vote for? Check out the official election website at your-choice-vir.virgov.xo.vr" + +/datum/lore/codex/page/article55 + name = "06/29/63 - Morpheus Cyberkinetics To Split Assets" + data = "The Morpheus Cyberkenetics Corporation is to split into two distinct entities operating under a single board of trustees, in light of their Almach branch's apparent involvement in the ongoing war after last month's 'unintentional' corporate drone strikes. Citing 'Severe communications disruptions' between its operations and assets on either side of the cordon since it was put in place last year, the SolGov-side corporation is to become 'Morpheus Sol', retaining most assets and current corporate headquarters, and its Almach counterpart 'Morpheus Shelf', which is to be based out of the administration station MAS Sophia Jr., located in the El system.'\ +

      \ + The principle victim of the Aetolian coup, Nanotrasen, has seen most of their considerable Almachi investment nationalized by the secessionist government, as has Xion and other major Almachi organizations. Most surviving corporate exclaves have been effectively written off by their parent company for the duration of the conflict, due to the severe difficulties effectively conducting trade across the militarized border. Before today, the sole exception was Morpheus, whose involvement in the secession prevented any seizing of their assets. It seems, however, that even the sardonic positronic corporation is not immune to the difficulties of doing business in the Almach Rim region.\ +

      \ + Member of the Board Chock Full of Sardines introduced the proposal by saying, 'Our goal here is not being shot. Together with leading economic scientists, we've devised a scheme that will allow us to be shot for illegal smuggling almost ninety percent less often.' They defended the confusing and offensive choice of name in 'Sophia Jr.', seemingly intended as an insult to longstanding rival Sophia, by claiming, 'It's absolutely hilarious.'" + +/datum/lore/codex/page/article56 + name = "06/30/63 - Almach Leak Confirms 'Super-weapon' in Whythe" + data = "Solar Confederate Government Intelligence has this afternoon confirmed the presence of a so-called 'Super-weapon' in the distant Whythe system, after an apparent intelligence leak was posted to the exonet in the early hours of this morning. According to a spokesperson for the Solar Fleet, the public were not made aware of the super-weapon as the military 'have no reason to believe that the weapon poses any threat to civilian targets within SolGov space at this time, and there is no reason to cause panic with what amounts to the announcement of an Almachi propaganda tool intended to sow discord with bold threats of overwhelming power. This morning's leak achieves nothing but serving the Association's schemes. Keeping this so-called super-weapon - and I hesitate to use that term - a secret seems to have been last on their list of priorities.'\ +

      \ + According to the intelligence documents released this morning and widely spread within minutes of upload, the 'super-weapon' is a colossal space-bound structure equipped with 'newly developed bluespace technology', though its exact purpose or capabilities have not been confirmed by either side.\ +

      \ + Additionally, the Solar Fleet has announced that an unnamed individual within the intelligence service has been placed under arrest in connection with the leak." + +/datum/lore/codex/page/article57 + name = "07/04/63 - Exit Polls Suggest Shadow Coalition Win in Vir" + data = "According to the first exit poll data released after Vir Gubernatorial voting closed at midnight, local favourite the Shadow Coalition is expected to win at least two representative seats, with incumbent representative Lusia Hainirsdottir taking a comfortable lead.\ +

      \ + Final results are not expected to be tallied until Saturday morning, but other frontrunners include the Icarus Front's Vani Jee and Mehmet Sao - running on drastically different platforms - alongside the Shadow Coalition's Selma Jorg. In an unexpected turn, sole Tajaran Candidate Kurah Zarshir of the Shadow Coalition has seen an immense surge in popularity among minority and more xenophilic voters. Could Vir be seeing its first Tajaran Representative? Experts say: 'Perhaps.'" + +/datum/lore/codex/page/article58 + name = "07/07/63 - Vir Election Results" + data = "The results of the 2563 Vir Gubernatorial Elections are as follows:\ +
      \ + Governor of Vir: Lusia Hainirsdottir (Shadow Coalition)\ +
      \ + Vir Colonial Assembly Representative: Vani Jee (Icarus Front)\ +
      \ + Vir Colonial Assembly Representative: Selma Jorg (Shadow Coalition)\ +
      \ + Other candidates ranked: Sao (4), Zarshir (5), Keldow (6), Singh (7), Moravec (8), Phaedrus (9), Lye (10), Savik (11), Square (12), Wekstrom (13)\ +

      \ + Voter turnout: 30,928,287 (63%)\ +

      \ + The greatest upset this election cycle has been the unexpected popularity of 'alien rights' candidate Kurah Zarshir, who was eliminated in favour of Mehmet Sao (Icarus Front) in the 8th round of vote transfers by a margin of just 30 votes, or 0.000096%, prompting a rigourous recount process to confirm the result. A difference at this stage could have resulted in a significantly different final line-up.\ +

      \ + This year's winners showed clear advantages in the first-choice votes, each gaining at least 15% of the popular vote before any transfers were calculated, though Sao made significant gains in the final count, falling only a few percent short of the Jorg's 3rd place position. By far the least popular candidate this cycle was Hal Wekstrom of the Sol Economic Organization, who received just 0.8% of the first-choice vote and was immediately eliminated. Also of note were Phaedrus, Apogee Lye and Yole Savik voters, each of whom had high (30%+) voter exhaustion rates, opting not to provide alternative choices; sending the message 'My candidate or none at all.'\ +

      \ + The elected are to be sworn in at a ceremony on Luna in two weeks time." + +/datum/lore/codex/page/article59 + name = "07/30/63 - Solar Fleet Data Breach" + data = "Last night, a number of files were spread on the Monsters From Beyond's exolife forums allegedly depicting the boarding and eventual scuttling of the SCG-TV Mariner's Cage during a voyage close to the Gavel system on the 12th of June, before the SCG had officially released any information regarding the event. The files contained undisclosed documents from the Solar Fleet investigation, some of which appear to contain audio and video recordings of the final moments of the crew before the vessel's bluespace drive was detonated. Due to the graphic violence depicted and their classified nature, we will not be sharing the files, however as a matter of public record we will explain the events recorded therein. The following description may be unsuitable for sensitive readers.\ +

      \ + First, the navigation crew detects a drive signature on an apparent intercept course with their own, originating from across the SCG-Almachi border. It was not a large vessel, and is assumed to be some form of autonomous drone. The crew disregards it as a low level threat, instead continuing on their trajectory, leaving only the standard point defense armament locked on. This proved to be a lethal mistake, as the vessel appeared and near-instantly began accelerating toward the Mariner's Cage, before impacting the fore weapons array. The recording is cut, due to what was likely a power surge, however upon reconnection, reports indicate no damage related to any known warhead was apparent, aside from the initial impactor. The crew mistakenly assumes it to be a failed suicide drone strike, and dispatches minimal security personnel, and a large complement of response engineers.\ +

      \ + Approximately thirty minutes after the response teams are dispatched to the impact zone, the teams begin losing contact, with those first arriving being the first to disappear. When the security responders intercept the path of communications blackouts, they are met with the blades of multiple Aetolian shock troopers. Two appear to be made from a 'living steel', with each limb taking the form of 'jagged cleavers' as one radio recording states, and three more of 'indeterminable classification'. The ship entered a red alert state, and moments later, the small contingent of marines aboard the supply vessel were dispatched to deal with the threat. All five members of the enemy boarding party were able to be rendered inert through sustained fire, though not without Sol casualties.\ +

      \ + According to the next recordings, approximately three hours after the incident, the vessel received orders to interrogate the boarding 'Aetotheans'. The two noted to appear as the officers of the squad were rejuvenated within sealed interrogation chambers reinforced with supplies on hand, apparently capable of stopping sustained fire from multiple energy weapons. The first individual was a 'sapphire' according to information from NanoTrasen correspondants. It refused to speak in Galactic Common, and instead utilized an unknown frequency of biological transmission, and internal charge shifts. The individual was moved to a more permanent cell within the vessel's brig for transport, and the second was rejuvenated. Only the first half of the interrogation, which lasted approximately two and a half minutes, compared to four hours for the first, was recovered. The individual is rejuvenated, and is engaged in discussion with the interrogating officer when it suddenly stands, emits what is described as a 'wail', and detonates, destroying the transmitting camera, and presumably killing the officers involved in direct interrogation.\ +

      \ + Final recordings originate from the ship's onboard A.I. housing, which was involved in continual discussions with presumably the 'sapphire', as it enacted the vessel's scuttling. It is unknown whether or not the individual was somehow capable of restoring the other individuals that fell in combat in order to free itself, or if it was able to incapacitate the transporting officers, and command crew of the vessel alone.\ +

      \ + The Solar Fleet has expressed 'regret' that the files were leaked in their complete form, and have assured the public that an official report was due for release in the coming weeks. Concerns of 'Aetothean' attacks on civilian targets have been dismissed as 'improbable', but have affirmed that 'the threat is being taken very seriously'." + +/datum/lore/codex/page/article60 + name = "08/03/63 - Hainirsdottir Sworn In As Governor of Vir" + data = "Following a short transitionary period for the previous administration, this year's election victors have been sworn in at an official ceremony at the Colonial Assembly Hall on Luna. During her welcoming address, Governor Hainirsdottir reaffirmed her plans for the future of the system, promising a 'Bright future for Vir as a hub for medical science.', and plans for an incentivisation program for the removal of invasive extra-terrestrial species that have long plagued the region - in particular the aggressive spiders that have become synonymous with certain regions of the Sivian wilderness.\ +

      \ + Additionally, the newly elected representatives announced expected, but none-the-less significant changes to the administrative staff of the system. Notable figures include two defeated election hopefuls: Kurah Zarshir has been selected as the Shadow Coalition's Culture Secretary for the system, while Mehmet Sao has been brought aboard by the Representative Vani Jee as the Vir Icarus Front's Internal Security Advisor. It is expected that the former candidates may use their positions to further certain goals from their own campaigns, but under the watchful eyes of their perhaps more moderate superiors." + +/datum/lore/codex/page/article61 + name = "08/04/63 - Former Independence Candidate Found Dead" + data = "It has been confirmed by a spokesperson for the Sivian Independence Front that a body found by hikers last week in the Ingolfskynn Mountains, approximately 200 miles northeast of New Reykjavik, belonged to party chair Yole Savik.\ +

      \ + Savik, 68 - who had run for Vir Representative in the recent election - had not been seen since the 14th of July, shortly after the results were announced. Party officials claim that Mr. Savik frequently made 'off the grid' trips into the Sivian wilderness and his absence had not been treated as suspicious until investigators approached them to confirm the identity of the body. According to police, though Yole was publicly known as a 'seasoned frontiersman', Savik had succumbed to exposure at least two weeks prior to the grisly discovery. His death is not being treated as suspicious." + +/datum/lore/codex/page/article62 + name = "08/07/63 - Almach Pirate Threat Vanishes - Analysts Baffled" + data = "Skrellian Xe'qua pirates operating in the far reaches of the Almach Association since the onset of hostilities last year, have inexplicably gone dark. The pirates, who were under close SolGov surveillance to monitor their impact on Almachi shipping, have drastically dropped in activity and numbers over the last month according to an official report released by the Solar Fleet today. The Fleet is unable to account for the cease in activity, which has now reached levels even lower than their pre-war baseline, as there have been no reports of Almach military operations in the area, nor any signs of decisive battle on the Almach border with pirate space.\ +

      \ + The drop in activity roughly coincides with the leaked information on an Almach 'Super-weapon' in Whythe, though military sources do not believe that the weapon has been deployed in any capacity at this time. According to Hasan Drust, an expert on Skrellian foreign policy, the 'only feasible explanation (is) major anti-piracy action undertaken by the Skrellian Far Kingdoms', who occupy the space beyond the Xe'qua pirates' known range. The reasoning behind this action now, against pirates who have historically only targeted human space is not entirely clear, though Drust suggests that it may simply be a coincidence as pirates would be a 'trivial issue' for Far Kingdom military might." + +/datum/lore/codex/page/article63 + name = "09/02/63 - Shock Almach Attack Routs Relan Front!" + data = "Following close to a month of reduced Almach activity, enemy Militia forces have today launched a staggering attack on Sol frontline forces in the region of the Relan system, disabling several SCG warships and forcing a major tactical retreat to Saint Columbia. The scale of this attack by Almach forces is unprecedented, but seems to be the result of the Association consolidating manpower previously dedicated to anti-piracy patrols on the far side of their territory. It is believed these vessels have become freed up due to the apparent but as of yet unconfirmed annihilation of Xe'qua criminal flotillas by Skrellian Far Kingdom police action.\ +

      \ + The Solar fleet had been in position to blockade the Relan system in the hopes of forcing the Free Relan Federation to surrender and withdraw from the Association, but was unprepared for what has been described as an 'all-out attack' on their positions, which left the vessels SCG-D Liu Bei, SCG-D Wodehouse, SCG-TV Ceylon Hartal and SCG-TV Apoxpalon disabled and unable to retreat with the bulk of our forces, as well as inflicting severe damage to several other craft. According to initial reports, the strikes on many of the afflicted ships closely resembled scenes from the controversial 'Aetothean shock attacks' on the SCG-TV Mariner's Cage this June, which saw the ruthless deployment of gene-altered Promethean 'super-soldiers' by the Almach Association.\ +

      \ + Fleet Admiral Ripon Latt, commanding officer of the assailed fleet, has confirmed that reinforcements are underway and the retreat 'shall not be a significant setback in the war effort', especially assuring citizens of the embattled Saint Columbia system and its neighbours that there is no cause for alarm and civilians have yet to be targeted.\ +

      \ + The fates of the four missing ships have not been confirmed, and though the Fleet has not yet made an official statement, Sol casualties are cautiously estimated to be in the hundreds." + +/datum/lore/codex/page/article64 + name = "09/23/63 - Fleet Refuses Inquiry Into Relan Losses" + data = "The SCG Fleet has refused to heed widespread calls from critics to launch an investigation into the heavy losses sustained by our forces in a major Almach attack early this month, citing that an investigation at this time would 'undermine the ongoing efforts of our troops in battles to come'.\ +

      \ + The attack, which took place on the 2nd of September and at current count resulted in the loss of a staggering 1281 Sol lives, quickly drew criticism from experts for 'the total unpreparedness' of the fleet despite their public claims that all vessels were 'battle ready and prepared for a coming offensive.'. The specifics of the fleets apparent failings have been the focus of much speculation in the intervening weeks, with the blame placed on everything from a critically inexperienced officer core, to ongoing redeployments to and from the recently expanded Hegemony border.\ +

      \ + Admiral Latt has condemned critics, stating that 'the last thing our brave troops need right now is murmuring from people who don't know the first thing what they're talking about. Their actions in following orders to fall back to the border have been nothing but commendable, and all effort was made to minimise loss of life. The fleet is undergoing reorganization at this time, and is in a better position than ever.'" + +/datum/lore/codex/page/article65 + name = "09/27/63 - Almach Bypass Saint Columbia In Brazen Gavel Attack!" + data = "Almach Association fleet forces entered the Gavel system this afternoon, reportedly having evaded interdicting Sol forces from Saint Columbia in an apparent effort to skirt the range of the MJOLNIR weapon system in Saint Columbia and cut off that system from major shipping routes. Current reports from the system capital in New Xanadu are that the majority of outlying civilian stations have surrendered to invading forces with only minor incident, but that skirmishes with local defence forces - including Sol Fleet detachments - are ongoing, and it is too early to remark on the outcome of the battle. Official military reports are scarce at this time, but the Fleet in Saint Columbia is 'on the move and ready to repel the invaders'.\ +

      \ + Accounts from the system's edge describe Almach forces 'firing indiscriminately' on anti-piracy emplacements including those mounted to the ILS Thurston, a Greyson Manufactories collection station with eight crew, killing all hands.\ +

      \ + Open fighting in the Gavel system marks the furthest Almach encroachment on Sol territory to date. The system, which is a stone's throw from the Oasis and Vir systems is best known for the destruction of the moonlet 'Requiem' by a rogue nanoswarm in 2289, which was successfully neutralized by government forces, and boasts only a small population relative to its neighbors." + +/datum/lore/codex/page/article66 + name = "10/01/63 - 'Judgement Day' As Gavel Falls!" + data = "The government of New Xanadu has surrendered to Association invaders following a disastrous relief effort by the Solar Fleet, whose interdiction vessels are believed to have been captured by the invading force. The manoeuvre leaves the bulk of the Sol fleet isolated in the Saint Columbia system - though a breakout is expected - and has led to widespread outrage in the Colonial Assembly. Critics of the war have damned the Fleet for their 'inability to fight a civilian rabble, gene-modded or otherwise' and renewed calls for a peaceful arrangement between the Solar Confederate Government and Association.\ +

      \ + ISA-5, current spokesperson for the Shadow Coalition has forwarded a motion today to resume discussions with Almachi heads of state, just hours after news of Gavel's surrender broke. The proposal which has yet to gain widespread traction, would call for a new ceasefire, and ISA-5 has stated they 'hope that a new agreement can be made to end the senseless loss of life over the particulars of a foreign government's right to autonomy.'.\ +

      \ + Executive Sifat Unar of the Emergent Intelligence Oversight has voiced immediate concern over the motion, criticising the use of 'foreign government' in reference to Almach; 'Our Fleet has suffered a few defeats, but this conflict goes deeper than mere lasers and shells and to surrender to torturers, mind-hackers, and Machiavellian machines at this stage would be insanity. To allow a seccessionist state, particularly one so unabashedly guilty of crimes against humanity that go far beyond even our modern definitions of 'Human Sanctity', to exist unquestioned a stone's throw from some of our most precious member states, would be a failing not only of this government, but of humanity that would echo through history like a great shameful dirge for all to hear.'\ +

      \ + A communications blackout has been instated on the Gavel system by the Almach Militia, though earlier reports indicate continued strikes on numerous civilian colonies who were unwilling, or unable to deactivate their automated defence systems prior to the invaders arrival." \ No newline at end of file diff --git a/code/modules/maps/tg/map_template.dm b/code/modules/maps/tg/map_template.dm index 63a597ac8e..132d76445c 100644 --- a/code/modules/maps/tg/map_template.dm +++ b/code/modules/maps/tg/map_template.dm @@ -225,8 +225,8 @@ orientation = pick(list(0, 90, 180, 270)) chosen_template.preload_size(chosen_template.mappath, orientation) - var/width_border = TRANSITIONEDGE + SUBMAP_MAP_EDGE_PAD + round(((orientation%180) ? chosen_template.height : chosen_template.width) / 2) // %180 catches East/West (90,270) rotations on true, North/South (0,180) rotations on false - var/height_border = TRANSITIONEDGE + SUBMAP_MAP_EDGE_PAD + round(((orientation%180) ? chosen_template.width : chosen_template.height) / 2) + var/width_border = SUBMAP_MAP_EDGE_PAD + round(((orientation%180) ? chosen_template.height : chosen_template.width) / 2) // %180 catches East/West (90,270) rotations on true, North/South (0,180) rotations on false //VOREStation Edit + var/height_border = SUBMAP_MAP_EDGE_PAD + round(((orientation%180) ? chosen_template.width : chosen_template.height) / 2) //VOREStation Edit var/z_level = pick(z_levels) var/turf/T = locate(rand(width_border, world.maxx - width_border), rand(height_border, world.maxy - height_border), z_level) var/valid = TRUE diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index f62356d83c..48cbf116c4 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -7,27 +7,27 @@ recipes = list() // If is_brittle() returns true, these are only good for a single strike. - recipes += new/datum/stack_recipe("[display_name] baseball bat", /obj/item/weapon/material/twohanded/baseballbat, 10, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] ashtray", /obj/item/weapon/material/ashtray, 2, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] spoon", /obj/item/weapon/material/kitchen/utensil/spoon/plastic, 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] armor plate", /obj/item/weapon/material/armor_plating, 1, time = 20, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] grave marker", /obj/item/weapon/material/gravemarker, 5, time = 50, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] ring", /obj/item/clothing/gloves/ring/material, 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] bracelet", /obj/item/clothing/accessory/bracelet/material, 1, on_floor = 1, supplied_material = "[name]") + recipes += new/datum/stack_recipe("[display_name] baseball bat", /obj/item/weapon/material/twohanded/baseballbat, 10, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] ashtray", /obj/item/weapon/material/ashtray, 2, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] spoon", /obj/item/weapon/material/kitchen/utensil/spoon/plastic, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] armor plate", /obj/item/weapon/material/armor_plating, 1, time = 20, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] grave marker", /obj/item/weapon/material/gravemarker, 5, time = 50, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] ring", /obj/item/clothing/gloves/ring/material, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] bracelet", /obj/item/clothing/accessory/bracelet/material, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) if(integrity>=50) - recipes += new/datum/stack_recipe("[display_name] door", /obj/structure/simple_door, 10, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] barricade", /obj/structure/barricade, 5, time = 50, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] stool", /obj/item/weapon/stool, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] chair", /obj/structure/bed/chair, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] bed", /obj/structure/bed, 2, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] double bed", /obj/structure/bed/double, 4, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] wall girders", /obj/structure/girder, 2, time = 50, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") + recipes += new/datum/stack_recipe("[display_name] door", /obj/structure/simple_door, 10, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] barricade", /obj/structure/barricade, 5, time = 50, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] stool", /obj/item/weapon/stool, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] chair", /obj/structure/bed/chair, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] bed", /obj/structure/bed, 2, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] double bed", /obj/structure/bed/double, 4, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] wall girders", /obj/structure/girder, 2, time = 50, one_per_turf = 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) if(hardness>50) - recipes += new/datum/stack_recipe("[display_name] fork", /obj/item/weapon/material/kitchen/utensil/fork/plastic, 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] knife", /obj/item/weapon/material/knife/plastic, 1, on_floor = 1, supplied_material = "[name]") - recipes += new/datum/stack_recipe("[display_name] blade", /obj/item/weapon/material/butterflyblade, 6, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]") + recipes += new/datum/stack_recipe("[display_name] fork", /obj/item/weapon/material/kitchen/utensil/fork/plastic, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] knife", /obj/item/weapon/material/knife/plastic, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] blade", /obj/item/weapon/material/butterflyblade, 6, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) /material/steel/generate_recipes() ..() @@ -89,11 +89,18 @@ recipes += new/datum/stack_recipe("small light fixture frame", /obj/item/frame/light/small, 1) recipes += new/datum/stack_recipe("floor lamp fixture frame", /obj/machinery/light_construct/flamp, 2) recipes += new/datum/stack_recipe("apc frame", /obj/item/frame/apc, 2) + recipes += new/datum/stack_recipe_list("modular computer frames", list( \ + new/datum/stack_recipe("modular console frame", /obj/item/modular_computer/console, 20),\ + new/datum/stack_recipe("modular telescreen frame", /obj/item/modular_computer/telescreen, 10),\ + new/datum/stack_recipe("modular laptop frame", /obj/item/modular_computer/laptop, 10),\ + new/datum/stack_recipe("modular tablet frame", /obj/item/modular_computer/tablet, 5),\ + )) recipes += new/datum/stack_recipe_list("filing cabinets", list( \ new/datum/stack_recipe("filing cabinet", /obj/structure/filingcabinet, 4, time = 20, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("tall filing cabinet", /obj/structure/filingcabinet/filingcabinet, 4, time = 20, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("chest drawer", /obj/structure/filingcabinet/chestdrawer, 4, time = 20, one_per_turf = 1, on_floor = 1), \ )) + recipes += new/datum/stack_recipe("desk bell", /obj/item/weapon/deskbell, 1, on_floor = 1, supplied_material = "[name]") /material/plasteel/generate_recipes() ..() @@ -104,60 +111,61 @@ recipes += new/datum/stack_recipe("roller bed", /obj/item/roller, 5, time = 30, on_floor = 1) recipes += new/datum/stack_recipe("whetstone", /obj/item/weapon/whetstone, 2, time = 10) -/material/sandstone/generate_recipes() +/material/stone/generate_recipes() ..() recipes += new/datum/stack_recipe("planting bed", /obj/machinery/portable_atmospherics/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1) /material/plastic/generate_recipes() ..() - recipes += new/datum/stack_recipe("plastic crate", /obj/structure/closet/crate/plastic, 10, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("plastic bag", /obj/item/weapon/storage/bag/plasticbag, 3, on_floor = 1) - recipes += new/datum/stack_recipe("blood pack", /obj/item/weapon/reagent_containers/blood/empty, 4, on_floor = 0) - recipes += new/datum/stack_recipe("reagent dispenser cartridge (large)", /obj/item/weapon/reagent_containers/chem_disp_cartridge, 5, on_floor=0) // 500u - recipes += new/datum/stack_recipe("reagent dispenser cartridge (med)", /obj/item/weapon/reagent_containers/chem_disp_cartridge/medium, 3, on_floor=0) // 250u - recipes += new/datum/stack_recipe("reagent dispenser cartridge (small)", /obj/item/weapon/reagent_containers/chem_disp_cartridge/small, 1, on_floor=0) // 100u - recipes += new/datum/stack_recipe("white floor tile", /obj/item/stack/tile/floor/white, 1, 4, 20) - recipes += new/datum/stack_recipe("freezer floor tile", /obj/item/stack/tile/floor/freezer, 1, 4, 20) - recipes += new/datum/stack_recipe("shower curtain", /obj/structure/curtain, 4, time = 15, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("plastic flaps", /obj/structure/plasticflaps, 4, time = 25, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("airtight plastic flaps", /obj/structure/plasticflaps/mining, 5, time = 25, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("water-cooler", /obj/structure/reagent_dispensers/water_cooler, 4, time = 10, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("lampshade", /obj/item/weapon/lampshade, 1, time = 1) + recipes += new/datum/stack_recipe("plastic crate", /obj/structure/closet/crate/plastic, 10, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("plastic bag", /obj/item/weapon/storage/bag/plasticbag, 3, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("blood pack", /obj/item/weapon/reagent_containers/blood/empty, 4, on_floor = 0, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("reagent dispenser cartridge (large)", /obj/item/weapon/reagent_containers/chem_disp_cartridge, 5, on_floor=0, pass_stack_color = TRUE) // 500u + recipes += new/datum/stack_recipe("reagent dispenser cartridge (med)", /obj/item/weapon/reagent_containers/chem_disp_cartridge/medium, 3, on_floor=0, pass_stack_color = TRUE) // 250u + recipes += new/datum/stack_recipe("reagent dispenser cartridge (small)", /obj/item/weapon/reagent_containers/chem_disp_cartridge/small, 1, on_floor=0, pass_stack_color = TRUE) // 100u + recipes += new/datum/stack_recipe("white floor tile", /obj/item/stack/tile/floor/white, 1, 4, 20, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("freezer floor tile", /obj/item/stack/tile/floor/freezer, 1, 4, 20, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("shower curtain", /obj/structure/curtain, 4, time = 15, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("plastic flaps", /obj/structure/plasticflaps, 4, time = 25, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("water-cooler", /obj/structure/reagent_dispensers/water_cooler, 4, time = 10, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("lampshade", /obj/item/weapon/lampshade, 1, time = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("plastic net", /obj/item/weapon/material/fishing_net, 25, time = 1 MINUTE, pass_stack_color = TRUE) /material/wood/generate_recipes() ..() - recipes += new/datum/stack_recipe("oar", /obj/item/weapon/oar, 2, time = 30, supplied_material = "[name]") - recipes += new/datum/stack_recipe("boat", /obj/vehicle/boat, 20, time = 10 SECONDS, supplied_material = "[name]") - recipes += new/datum/stack_recipe("dragon boat", /obj/vehicle/boat/dragon, 50, time = 30 SECONDS, supplied_material = "[name]") - recipes += new/datum/stack_recipe("wooden sandals", /obj/item/clothing/shoes/sandal, 1) - recipes += new/datum/stack_recipe("wood circlet", /obj/item/clothing/head/woodcirclet, 1) - recipes += new/datum/stack_recipe("clipboard", /obj/item/weapon/clipboard, 1) - recipes += new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20) - recipes += new/datum/stack_recipe("wooden chair", /obj/structure/bed/chair/wood, 3, time = 10, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("crossbow frame", /obj/item/weapon/crossbowframe, 5, time = 25, one_per_turf = 0, on_floor = 0) - recipes += new/datum/stack_recipe("coffin", /obj/structure/closet/coffin, 5, time = 15, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("beehive assembly", /obj/item/beehive_assembly, 4) - recipes += new/datum/stack_recipe("beehive frame", /obj/item/honey_frame, 1) - recipes += new/datum/stack_recipe("book shelf", /obj/structure/bookcase, 5, time = 15, one_per_turf = 1, on_floor = 1) - recipes += new/datum/stack_recipe("noticeboard frame", /obj/item/frame/noticeboard, 4, time = 5, one_per_turf = 0, on_floor = 1) - recipes += new/datum/stack_recipe("wooden bucket", /obj/item/weapon/reagent_containers/glass/bucket/wood, 2, time = 4, one_per_turf = 0, on_floor = 0) - recipes += new/datum/stack_recipe("coilgun stock", /obj/item/weapon/coilgun_assembly, 5) + recipes += new/datum/stack_recipe("oar", /obj/item/weapon/oar, 2, time = 30, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("boat", /obj/vehicle/boat, 20, time = 10 SECONDS, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("dragon boat", /obj/vehicle/boat/dragon, 50, time = 30 SECONDS, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wooden sandals", /obj/item/clothing/shoes/sandal, 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wood circlet", /obj/item/clothing/head/woodcirclet, 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("clipboard", /obj/item/weapon/clipboard, 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wooden chair", /obj/structure/bed/chair/wood, 3, time = 10, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("crossbow frame", /obj/item/weapon/crossbowframe, 5, time = 25, one_per_turf = 0, on_floor = 0, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("coffin", /obj/structure/closet/coffin, 5, time = 15, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("beehive assembly", /obj/item/beehive_assembly, 4, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("beehive frame", /obj/item/honey_frame, 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("book shelf", /obj/structure/bookcase, 5, time = 15, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("noticeboard frame", /obj/item/frame/noticeboard, 4, time = 5, one_per_turf = 0, on_floor = 1, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wooden bucket", /obj/item/weapon/reagent_containers/glass/bucket/wood, 2, time = 4, one_per_turf = 0, on_floor = 0, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("coilgun stock", /obj/item/weapon/coilgun_assembly, 5, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("crude fishing rod", /obj/item/weapon/material/fishing_rod/built, 8, time = 10 SECONDS, pass_stack_color = TRUE) /material/wood/log/generate_recipes() recipes = list() - recipes += new/datum/stack_recipe("bonfire", /obj/structure/bonfire, 5, time = 50, supplied_material = "[name]") + recipes += new/datum/stack_recipe("bonfire", /obj/structure/bonfire, 5, time = 50, supplied_material = "[name]", pass_stack_color = TRUE) /material/cardboard/generate_recipes() ..() - recipes += new/datum/stack_recipe("box", /obj/item/weapon/storage/box) - recipes += new/datum/stack_recipe("donut box", /obj/item/weapon/storage/box/donut/empty) - recipes += new/datum/stack_recipe("egg box", /obj/item/weapon/storage/fancy/egg_box) - recipes += new/datum/stack_recipe("light tubes box", /obj/item/weapon/storage/box/lights/tubes) - recipes += new/datum/stack_recipe("light bulbs box", /obj/item/weapon/storage/box/lights/bulbs) - recipes += new/datum/stack_recipe("mouse traps box", /obj/item/weapon/storage/box/mousetraps) - recipes += new/datum/stack_recipe("cardborg suit", /obj/item/clothing/suit/cardborg, 3) - recipes += new/datum/stack_recipe("cardborg helmet", /obj/item/clothing/head/cardborg) - recipes += new/datum/stack_recipe("pizza box", /obj/item/pizzabox) + recipes += new/datum/stack_recipe("box", /obj/item/weapon/storage/box, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("donut box", /obj/item/weapon/storage/box/donut/empty, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("egg box", /obj/item/weapon/storage/fancy/egg_box, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("light tubes box", /obj/item/weapon/storage/box/lights/tubes, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("light bulbs box", /obj/item/weapon/storage/box/lights/bulbs, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("mouse traps box", /obj/item/weapon/storage/box/mousetraps, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("cardborg suit", /obj/item/clothing/suit/cardborg, 3, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("cardborg helmet", /obj/item/clothing/head/cardborg, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("pizza box", /obj/item/pizzabox, pass_stack_color = TRUE) recipes += new/datum/stack_recipe_list("folders",list( \ new/datum/stack_recipe("blue folder", /obj/item/weapon/folder/blue), \ new/datum/stack_recipe("grey folder", /obj/item/weapon/folder), \ @@ -187,10 +195,33 @@ /material/wood/sif/generate_recipes() ..() - recipes += new/datum/stack_recipe("alien wood floor tile", /obj/item/stack/tile/wood/sif, 1, 4, 20) - recipes -= new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20) - recipes -= new/datum/stack_recipe("wooden chair", /obj/structure/bed/chair/wood, 3, time = 10, one_per_turf = 1, on_floor = 1) + recipes += new/datum/stack_recipe("alien wood floor tile", /obj/item/stack/tile/wood/sif, 1, 4, 20, pass_stack_color = TRUE) + for(var/datum/stack_recipe/r_recipe in recipes) + if(r_recipe.title == "wood floor tile") + recipes -= r_recipe + continue + if(r_recipe.title == "wooden chair") + recipes -= r_recipe + continue /material/supermatter/generate_recipes() recipes = list() recipes += new/datum/stack_recipe("supermatter shard", /obj/machinery/power/supermatter/shard, 30 , one_per_turf = 1, time = 600, on_floor = 1) + +/material/cloth/generate_recipes() + recipes = list() + recipes += new/datum/stack_recipe("woven net", /obj/item/weapon/material/fishing_net, 10, time = 30 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("bedsheet", /obj/item/weapon/bedsheet, 10, time = 30 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("uniform", /obj/item/clothing/under/color/white, 8, time = 15 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("foot wraps", /obj/item/clothing/shoes/footwraps, 2, time = 5 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("gloves", /obj/item/clothing/gloves/white, 2, time = 5 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wig", /obj/item/clothing/head/powdered_wig, 4, time = 10 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("philosopher's wig", /obj/item/clothing/head/philosopher_wig, 50, time = 2 MINUTES, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("taqiyah", /obj/item/clothing/head/taqiyah, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("turban", /obj/item/clothing/head/turban, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("hijab", /obj/item/clothing/head/hijab, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("kippa", /obj/item/clothing/head/kippa, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("scarf", /obj/item/clothing/accessory/scarf/white, 4, time = 5 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("baggy pants", /obj/item/clothing/under/pants/baggy/white, 8, time = 10 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("belt pouch", /obj/item/weapon/storage/belt/fannypack/white, 25, time = 1 MINUTE, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("crude bandage", /obj/item/stack/medical/crude_pack, 1, time = 2 SECONDS, pass_stack_color = TRUE) diff --git a/code/modules/materials/material_recipes_vr.dm b/code/modules/materials/material_recipes_vr.dm index 9f8a323407..918dff0db1 100644 --- a/code/modules/materials/material_recipes_vr.dm +++ b/code/modules/materials/material_recipes_vr.dm @@ -2,3 +2,13 @@ /material/steel/generate_recipes() . = ..() recipes += new/datum/stack_recipe("light switch frame", /obj/item/frame/lightswitch, 2) + recipes += new/datum/stack_recipe_list("sofas", list( \ + new/datum/stack_recipe("sofa middle", /obj/structure/bed/chair/sofa, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("sofa left", /obj/structure/bed/chair/sofa/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("sofa right", /obj/structure/bed/chair/sofa/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("sofa corner", /obj/structure/bed/chair/sofa/corner, 1, one_per_turf = 1, on_floor = 1), \ + )) + +/material/durasteel/generate_recipes() + . = ..() + recipes += new/datum/stack_recipe("durasteel fishing rod", /obj/item/weapon/material/fishing_rod/modern/strong, 2) diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 1035c70eba..31a3447bf2 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -99,7 +99,7 @@ icon_state = "sheet-adamantine" default_type = "lead" apply_colour = 1 - no_variants = TRUE + no_variants = FALSE /obj/item/stack/material/sandstone name = "sandstone brick" @@ -275,8 +275,10 @@ return /obj/item/stack/material/supermatter/attack_hand(mob/user) + . = ..() + update_mass() - radiation_repository.radiate(src, 5 + amount) + SSradiation.radiate(src, 5 + amount) var/mob/living/M = user if(!istype(M)) return @@ -303,17 +305,18 @@ /obj/item/stack/material/supermatter/ex_act(severity) // An incredibly hard to manufacture material, SM chunks are unstable by their 'stabilized' nature. if(prob((4 / severity) * 20)) - radiation_repository.radiate(get_turf(src), amount * 4) + SSradiation.radiate(get_turf(src), amount * 4) explosion(get_turf(src),round(amount / 12) , round(amount / 6), round(amount / 3), round(amount / 25)) qdel(src) return - radiation_repository.radiate(get_turf(src), amount * 2) + SSradiation.radiate(get_turf(src), amount * 2) ..() /obj/item/stack/material/wood name = "wooden plank" icon_state = "sheet-wood" default_type = MAT_WOOD + strict_color_stacking = TRUE /obj/item/stack/material/wood/sif name = "alien wooden plank" @@ -365,12 +368,16 @@ icon_state = "sheet-cloth" default_type = "cloth" no_variants = FALSE + pass_color = TRUE + strict_color_stacking = TRUE /obj/item/stack/material/cardboard name = "cardboard" icon_state = "sheet-card" default_type = "cardboard" no_variants = FALSE + pass_color = TRUE + strict_color_stacking = TRUE /obj/item/stack/material/snow name = "snow" @@ -390,6 +397,8 @@ icon_state = "sheet-leather" default_type = "leather" no_variants = FALSE + pass_color = TRUE + strict_color_stacking = TRUE /obj/item/stack/material/glass name = "glass" diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm index 5428e110db..307b52bb90 100644 --- a/code/modules/materials/materials.dm +++ b/code/modules/materials/materials.dm @@ -94,6 +94,7 @@ var/list/name_to_material var/door_icon_base = "metal" // Door base icon tag. See header. var/icon_reinf = "reinf_metal" // Overlay used var/list/stack_origin_tech = list(TECH_MATERIAL = 1) // Research level for stacks. + var/pass_stack_colors = FALSE // Will stacks made from this material pass their colors onto objects? // Attributes var/cut_delay = 0 // Delay in ticks when cutting through this wall. @@ -107,7 +108,7 @@ var/list/name_to_material var/explosion_resistance = 5 // Only used by walls currently. var/negation = 0 // Objects that respect this will randomly absorb impacts with this var as the percent chance. var/spatial_instability = 0 // Objects that have trouble staying in the same physical space by sheer laws of nature have this. Percent for respecting items to cause teleportation. - var/conductive = 1 // Objects with this var add CONDUCTS to flags on spawn. + var/conductive = 1 // Objects without this var add NOCONDUCT to flags on spawn. var/conductivity = null // How conductive the material is. Iron acts as the baseline, at 10. var/list/composite_material // If set, object matter var will be a list containing these values. var/luminescence @@ -259,6 +260,7 @@ var/list/name_to_material icon_colour = "#00FFE1" opacity = 0.4 reflectivity = 0.6 + conductive = 0 conductivity = 1 shard_type = SHARD_SHARD tableslam_noise = 'sound/effects/Glasshit.ogg' @@ -350,6 +352,7 @@ var/list/name_to_material weight = 22 hardness = 55 protectiveness = 5 // 20% + conductive = 0 conductivity = 5 door_icon_base = "stone" sheet_singular_name = "brick" @@ -436,7 +439,7 @@ var/list/name_to_material // Very rare alloy that is reflective, should be used sparingly. /material/durasteel name = "durasteel" - stack_type = /obj/item/stack/material/durasteel + stack_type = /obj/item/stack/material/durasteel/hull integrity = 600 melting_point = 7000 icon_base = "metal" @@ -458,6 +461,9 @@ var/list/name_to_material explosion_resistance = 90 reflectivity = 0.9 +/material/durasteel/hull/place_sheet(var/turf/target) //Deconstructed into normal durasteel sheets. + new /obj/item/stack/material/durasteel(target) + /material/plasteel/titanium name = MAT_TITANIUM stack_type = /obj/item/stack/material/titanium @@ -473,6 +479,9 @@ var/list/name_to_material icon_base = "hull" icon_reinf = "reinf_mesh" +/material/plasteel/titanium/hull/place_sheet(var/turf/target) //Deconstructed into normal titanium sheets. + new /obj/item/stack/material/titanium(target) + /material/glass name = "glass" stack_type = /obj/item/stack/material/glass @@ -485,6 +494,7 @@ var/list/name_to_material hardness = 30 weight = 15 protectiveness = 0 // 0% + conductive = 0 conductivity = 1 // Glass shards don't conduct. door_icon_base = "stone" destruction_desc = "shatters" @@ -626,6 +636,7 @@ var/list/name_to_material hardness = 10 weight = 12 protectiveness = 5 // 20% + conductive = 0 conductivity = 2 // For the sake of material armor diversity, we're gonna pretend this plastic is a good insulator. melting_point = T0C+371 //assuming heat resistant plastic stack_origin_tech = list(TECH_MATERIAL = 3) @@ -643,6 +654,7 @@ var/list/name_to_material stack_origin_tech = list(TECH_MATERIAL = 5) sheet_singular_name = "ingot" sheet_plural_name = "ingots" + conductivity = 100 /material/tritium name = "tritium" @@ -652,6 +664,7 @@ var/list/name_to_material sheet_singular_name = "ingot" sheet_plural_name = "ingots" is_fusion_fuel = 1 + conductive = 0 /material/deuterium name = "deuterium" @@ -661,6 +674,7 @@ var/list/name_to_material sheet_singular_name = "ingot" sheet_plural_name = "ingots" is_fusion_fuel = 1 + conductive = 0 /material/mhydrogen name = "mhydrogen" @@ -730,6 +744,7 @@ var/list/name_to_material icon_reinf = "reinf_metal" protectiveness = 60 integrity = 300 + conductive = 0 conductivity = 1.5 hardness = 90 shard_type = SHARD_SHARD @@ -758,6 +773,7 @@ var/list/name_to_material weight = 30 hardness = 45 negation = 2 + conductive = 0 conductivity = 5 reflectivity = 0.5 radiation_resistance = 20 @@ -813,6 +829,7 @@ var/list/name_to_material melting_point = T0C+300 sheet_singular_name = "blob" sheet_plural_name = "blobs" + conductive = 0 /material/resin/can_open_material_door(var/mob/living/user) var/mob/living/carbon/M = user @@ -833,6 +850,7 @@ var/list/name_to_material hardness = 15 weight = 18 protectiveness = 8 // 28% + conductive = 0 conductivity = 1 melting_point = T0C+300 //okay, not melting in this case, but hot enough to destroy wood ignition_point = T0C+288 @@ -849,6 +867,7 @@ var/list/name_to_material stack_type = /obj/item/stack/material/log sheet_singular_name = null sheet_plural_name = "pile" + pass_stack_colors = TRUE /material/wood/log/sif name = MAT_SIFLOG @@ -864,7 +883,7 @@ var/list/name_to_material /material/wood/sif name = MAT_SIFWOOD -// stack_type = /obj/item/stack/material/wood/sif + stack_type = /obj/item/stack/material/wood/sif icon_colour = "#0099cc" // Cyan-ish stack_origin_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2) // Alien wood would presumably be more interesting to the analyzer. @@ -879,12 +898,14 @@ var/list/name_to_material hardness = 1 weight = 1 protectiveness = 0 // 0% + conductive = 0 ignition_point = T0C+232 //"the temperature at which book-paper catches fire, and burns." close enough melting_point = T0C+232 //temperature at which cardboard walls would be destroyed stack_origin_tech = list(TECH_MATERIAL = 1) door_icon_base = "wood" destruction_desc = "crumples" radiation_resistance = 1 + pass_stack_colors = TRUE /material/snow name = MAT_SNOW @@ -930,6 +951,8 @@ var/list/name_to_material melting_point = T0C+300 protectiveness = 1 // 4% flags = MATERIAL_PADDING + conductive = 0 + pass_stack_colors = TRUE /material/cult name = "cult" @@ -940,6 +963,7 @@ var/list/name_to_material shard_type = SHARD_STONE_PIECE sheet_singular_name = "brick" sheet_plural_name = "bricks" + conductive = 0 /material/cult/place_dismantled_girder(var/turf/target) new /obj/structure/girder/cult(target, "cult") @@ -963,6 +987,7 @@ var/list/name_to_material ignition_point = T0C+300 melting_point = T0C+300 protectiveness = 3 // 13% + conductive = 0 /material/carpet name = "carpet" @@ -975,6 +1000,7 @@ var/list/name_to_material sheet_singular_name = "tile" sheet_plural_name = "tiles" protectiveness = 1 // 4% + conductive = 0 /material/cotton name = "cotton" @@ -984,6 +1010,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 // This all needs to be OOP'd and use inheritence if its ever used in the future. /material/cloth_teal @@ -995,6 +1022,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/cloth_black name = "black" @@ -1005,6 +1033,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/cloth_green name = "green" @@ -1015,6 +1044,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/cloth_puple name = "purple" @@ -1025,6 +1055,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/cloth_blue name = "blue" @@ -1035,6 +1066,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/cloth_beige name = "beige" @@ -1045,6 +1077,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/cloth_lime name = "lime" @@ -1055,6 +1088,7 @@ var/list/name_to_material ignition_point = T0C+232 melting_point = T0C+300 protectiveness = 1 // 4% + conductive = 0 /material/toy_foam name = "foam" @@ -1067,3 +1101,4 @@ var/list/name_to_material hardness = 1 weight = 1 protectiveness = 0 // 0% + conductive = 0 diff --git a/code/modules/metric/activity.dm b/code/modules/metric/activity.dm index c4acdc57ec..5bdc33f47a 100644 --- a/code/modules/metric/activity.dm +++ b/code/modules/metric/activity.dm @@ -80,3 +80,14 @@ num++ if(num) . = round(. / num, 0.1) + +/datum/metric/proc/assess_all_outdoor_mobs() + . = 0 + var/num = 0 + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(istype(T) && !istype(T, /turf/space) && T.outdoors) + . += assess_player_activity(L) + num++ + if(num) + . = round(. / num, 0.1) diff --git a/code/modules/metric/count.dm b/code/modules/metric/count.dm new file mode 100644 index 0000000000..3900aaaa97 --- /dev/null +++ b/code/modules/metric/count.dm @@ -0,0 +1,23 @@ +/* + * Procs for counting active players in different situations. Returns the number of active players within the given cutoff. + */ + +/datum/metric/proc/count_all_outdoor_mobs(var/cutoff = 75) + var/num = 0 + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(istype(T) && !istype(T, /turf/space) && T.outdoors) + if(assess_player_activity(L) >= cutoff) + num++ + return num + +/datum/metric/proc/count_all_space_mobs(var/cutoff = 75, var/respect_z = TRUE) + var/num = 0 + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(istype(T, /turf/space) && istype(T.loc, /area/space)) + if(respect_z && !(L.z in using_map.station_levels)) + continue + if(assess_player_activity(L) >= cutoff) + num++ + return num diff --git a/code/modules/metric/department.dm b/code/modules/metric/department.dm index ef146de506..b6996d229c 100644 --- a/code/modules/metric/department.dm +++ b/code/modules/metric/department.dm @@ -69,4 +69,4 @@ for(var/mob/M in player_list) if(guess_department(M) != department) // Ignore people outside the department we're counting. continue - . += 1 \ No newline at end of file + . += 1 diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index 08580c7f97..bdfab8f92b 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -318,13 +318,13 @@ name = "mining drill brace" desc = "A machinery brace for an industrial drill. It looks easily two feet thick." icon_state = "mining_brace" + circuit = /obj/item/weapon/circuitboard/miningdrillbrace var/obj/machinery/mining/drill/connected /obj/machinery/mining/brace/New() ..() component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/miningdrillbrace(src) /obj/machinery/mining/brace/attackby(obj/item/weapon/W as obj, mob/user as mob) if(connected && connected.active) diff --git a/code/modules/mining/drilling/scanner.dm b/code/modules/mining/drilling/scanner.dm index 56f371fe91..bbcc65cc16 100644 --- a/code/modules/mining/drilling/scanner.dm +++ b/code/modules/mining/drilling/scanner.dm @@ -6,14 +6,18 @@ item_state = "electronic" origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) matter = list(DEFAULT_WALL_MATERIAL = 150) + var/scan_time = 5 SECONDS /obj/item/weapon/mining_scanner/attack_self(mob/user as mob) user << "You begin sweeping \the [src] about, scanning for metal deposits." playsound(loc, 'sound/items/goggles_charge.ogg', 50, 1, -6) - if(!do_after(user, 50)) + if(!do_after(user, scan_time)) return + ScanTurf(user, get_turf(user)) + +/obj/item/weapon/mining_scanner/proc/ScanTurf(var/atom/target, var/mob/user, var/exact = FALSE) var/list/metals = list( "surface minerals" = 0, "precious metals" = 0, @@ -22,7 +26,9 @@ "anomalous matter" = 0 ) - for(var/turf/simulated/T in range(2, get_turf(user))) + var/turf/Turf = get_turf(target) + + for(var/turf/simulated/T in range(2, Turf)) if(!T.has_resources) continue @@ -39,14 +45,18 @@ if(ore_type) metals[ore_type] += T.resources[metal] - user << "\icon[src] The scanner beeps and displays a readout." + to_chat(user, "\icon[src] The scanner beeps and displays a readout.") for(var/ore_type in metals) var/result = "no sign" - switch(metals[ore_type]) - if(1 to 25) result = "trace amounts" - if(26 to 75) result = "significant amounts" - if(76 to INFINITY) result = "huge quantities" + if(!exact) + switch(metals[ore_type]) + if(1 to 25) result = "trace amounts" + if(26 to 75) result = "significant amounts" + if(76 to INFINITY) result = "huge quantities" - user << "- [result] of [ore_type]." \ No newline at end of file + else + result = metals[ore_type] + + to_chat(user, "- [result] of [ore_type].") diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm index d07a52a2f2..fa5fcc7397 100644 --- a/code/modules/mining/fulton.dm +++ b/code/modules/mining/fulton.dm @@ -9,7 +9,7 @@ var/global/list/total_extraction_beacons = list() var/obj/structure/extraction_point/beacon var/list/beacon_networks = list("station") var/uses_left = 3 - var/can_use_indoors + var/can_use_indoors = FALSE var/safe_for_living_creatures = 1 /obj/item/extraction_pack/examine() @@ -145,7 +145,9 @@ var/global/list/total_extraction_beacons = list() icon_state = "subspace_amplifier" /obj/item/fulton_core/attack_self(mob/user) - if(do_after(user,15,target = user) && !QDELETED(src)) + var/turf/T = get_turf(user) + var/outdoors = T.outdoors + if(do_after(user,15,target = user) && !QDELETED(src) && outdoors) new /obj/structure/extraction_point(get_turf(user)) qdel(src) diff --git a/code/modules/mining/mine_outcrops.dm b/code/modules/mining/mine_outcrops.dm new file mode 100644 index 0000000000..5ce5c5bc94 --- /dev/null +++ b/code/modules/mining/mine_outcrops.dm @@ -0,0 +1,117 @@ +/obj/structure/outcrop + name = "outcrop" + desc = "A boring rocky outcrop." + icon = 'icons/obj/outcrop.dmi' + density = 1 + throwpass = 1 + climbable = 1 + anchored = 1 + icon_state = "outcrop" + var/mindrop = 5 + var/upperdrop = 10 + var/outcropdrop = /obj/item/weapon/ore/glass + +/obj/structure/outcrop/Initialize() + . = ..() + if(prob(1)) + overlays += image(icon, "[initial(icon_state)]-egg") + +/obj/structure/outcrop/diamond + name = "shiny outcrop" + desc = "A shiny rocky outcrop." + icon_state = "outcrop-diamond" + mindrop = 2 + upperdrop = 4 + outcropdrop = /obj/item/weapon/ore/diamond + +/obj/structure/outcrop/phoron + name = "shiny outcrop" + desc = "A shiny rocky outcrop." + icon_state = "outcrop-phoron" + mindrop = 4 + upperdrop = 8 + outcropdrop = /obj/item/weapon/ore/phoron + +/obj/structure/outcrop/iron + name = "rugged outcrop" + desc = "A rugged rocky outcrop." + icon_state = "outcrop-iron" + mindrop = 10 + upperdrop = 20 + outcropdrop = /obj/item/weapon/ore/iron + +/obj/structure/outcrop/coal + name = "rugged outcrop" + desc = "A rugged rocky outcrop." + icon_state = "outcrop-coal" + mindrop = 10 + upperdrop = 20 + outcropdrop = /obj/item/weapon/ore/coal + +/obj/structure/outcrop/lead + name = "rugged outcrop" + desc = "A rugged rocky outcrop." + icon_state = "outcrop-lead" + mindrop = 2 + upperdrop = 5 + outcropdrop = /obj/item/weapon/ore/lead + +/obj/structure/outcrop/gold + name = "hollow outcrop" + desc = "A hollow rocky outcrop." + icon_state = "outcrop-gold" + mindrop = 4 + upperdrop = 6 + outcropdrop = /obj/item/weapon/ore/gold + +/obj/structure/outcrop/silver + name = "hollow outcrop" + desc = "A hollow rocky outcrop." + icon_state = "outcrop-silver" + mindrop = 6 + upperdrop = 8 + outcropdrop = /obj/item/weapon/ore/silver + +/obj/structure/outcrop/platinum + name = "hollow outcrop" + desc = "A hollow rocky outcrop." + icon_state = "outcrop-platinum" + mindrop = 2 + upperdrop = 5 + outcropdrop = /obj/item/weapon/ore/osmium + +/obj/structure/outcrop/uranium + name = "spiky outcrop" + desc = "A spiky rocky outcrop, it glows faintly." + icon_state = "outcrop-uranium" + mindrop = 4 + upperdrop = 8 + outcropdrop = /obj/item/weapon/ore/uranium + +/obj/structure/outcrop/attackby(obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/pickaxe)) + to_chat(user, "[user] begins to hack away at \the [src].") + if(do_after(user,40)) + to_chat(user, "You have finished digging!") + for(var/i=0;i<(rand(mindrop,upperdrop));i++) + new outcropdrop(get_turf(src)) + qdel(src) + return + +/obj/random/outcrop //In case you want an outcrop without pre-determining the type of ore. + name = "random rock outcrop" + desc = "This is a random rock outcrop." + icon = 'icons/obj/outcrop.dmi' + icon_state = "outcrop-random" + +/obj/random/outcrop/item_to_spawn() + return pick(prob(100);/obj/structure/outcrop, + prob(100);/obj/structure/outcrop/iron, + prob(100);/obj/structure/outcrop/coal, + prob(65);/obj/structure/outcrop/silver, + prob(50);/obj/structure/outcrop/gold, + prob(30);/obj/structure/outcrop/uranium, + prob(30);/obj/structure/outcrop/phoron, + prob(7);/obj/structure/outcrop/diamond, + prob(15);/obj/structure/outcrop/platinum, + prob(15);/obj/structure/outcrop/lead) \ No newline at end of file diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index 87b613a8e3..b006808a1e 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -11,6 +11,10 @@ var/list/mining_overlay_cache = list() name = "rock" icon = 'icons/turf/walls.dmi' icon_state = "rock" + var/rock_side_icon_state = "rock_side" + var/sand_icon_state = "asteroid" + var/rock_icon_state = "rock" + var/random_icon = 0 oxygen = 0 nitrogen = 0 opacity = 1 @@ -54,6 +58,14 @@ var/list/mining_overlay_cache = list() has_resources = 1 +// Alternative rock wall sprites. +/turf/simulated/mineral/light + icon_state = "rock-light" + rock_side_icon_state = "rock_side-light" + sand_icon_state = "sand-light" + rock_icon_state = "rock-light" + random_icon = 1 + /turf/simulated/mineral/ignore_mapgen ignore_mapgen = 1 @@ -66,6 +78,23 @@ var/list/mining_overlay_cache = list() blocks_air = 0 can_build_into_floor = TRUE +//Alternative sand floor sprite. +turf/simulated/mineral/floor/light + icon_state = "sand-light" + sand_icon_state = "sand-light" + +turf/simulated/mineral/floor/light_border + icon_state = "sand-light-border" + sand_icon_state = "sand-light-border" + +turf/simulated/mineral/floor/light_nub + icon_state = "sand-light-nub" + sand_icon_state = "sand-light-nub" + +turf/simulated/mineral/floor/light_corner + icon_state = "sand-light-corner" + sand_icon_state = "sand-light-corner" + /turf/simulated/mineral/floor/ignore_mapgen ignore_mapgen = 1 @@ -130,6 +159,9 @@ var/list/mining_overlay_cache = list() update_icon(1) if(density && mineral) . = INITIALIZE_HINT_LATELOAD + if(random_icon) + dir = pick(alldirs) + . = INITIALIZE_HINT_LATELOAD /turf/simulated/mineral/LateInitialize() if(density && mineral) @@ -147,13 +179,13 @@ var/list/mining_overlay_cache = list() name = "rock" icon = 'icons/turf/walls.dmi' - icon_state = "rock" + icon_state = rock_icon_state //Apply overlays if we should have borders for(var/direction in cardinal) var/turf/T = get_step(src,direction) if(istype(T) && !T.density) - add_overlay(get_cached_border("rock_side",direction,icon,"rock_side")) + add_overlay(get_cached_border(rock_side_icon_state,direction,icon,rock_side_icon_state)) if(archaeo_overlay) add_overlay(archaeo_overlay) @@ -165,7 +197,7 @@ var/list/mining_overlay_cache = list() else name = "sand" icon = 'icons/turf/flooring/asteroid.dmi' - icon_state = "asteroid" + icon_state = sand_icon_state if(sand_dug) add_overlay("dug_overlay") @@ -179,7 +211,7 @@ var/list/mining_overlay_cache = list() else var/turf/T = get_step(src, direction) if(istype(T) && T.density) - add_overlay(get_cached_border("rock_side",direction,'icons/turf/walls.dmi',"rock_side")) + add_overlay(get_cached_border(rock_side_icon_state,direction,'icons/turf/walls.dmi',rock_side_icon_state)) if(overlay_detail) add_overlay('icons/turf/flooring/decals.dmi',overlay_detail) @@ -542,7 +574,7 @@ var/list/mining_overlay_cache = list() M.flash_eyes() if(prob(50)) M.Stun(5) - radiation_repository.flat_radiate(src, 25, 100) + SSradiation.flat_radiate(src, 25, 100) if(prob(25)) excavate_find(prob(5), finds[1]) else if(rand(1,500) == 1) @@ -559,9 +591,10 @@ var/list/mining_overlay_cache = list() if(is_clean) X = new /obj/item/weapon/archaeological_find(src, new_item_type = F.find_type) else - X = new /obj/item/weapon/ore/strangerock(src, inside_item_type = F.find_type) + X = new /obj/item/weapon/strangerock(src, inside_item_type = F.find_type) geologic_data.UpdateNearbyArtifactInfo(src) - X:geologic_data = geologic_data + var/obj/item/weapon/strangerock/SR = X + SR.geologic_data = geologic_data //some find types delete the /obj/item/weapon/archaeological_find and replace it with something else, this handles when that happens //yuck @@ -606,7 +639,7 @@ var/list/mining_overlay_cache = list() new /obj/item/stack/material/uranium(src, rand(5,25)) /turf/simulated/mineral/proc/make_ore(var/rare_ore) - if(mineral || ignore_mapgen) //VOREStation Edit - Makes sense, doesn't it? + if(mineral || ignore_mapgen || ignore_oregen) //VOREStation Edit - Makes sense, doesn't it? return var/mineral_name diff --git a/code/modules/mining/mine_turfs_vr.dm b/code/modules/mining/mine_turfs_vr.dm new file mode 100644 index 0000000000..e0cb524936 --- /dev/null +++ b/code/modules/mining/mine_turfs_vr.dm @@ -0,0 +1,15 @@ +/turf/simulated/mineral + var/ignore_oregen = FALSE + var/ignore_cavegen = FALSE + +/turf/simulated/mineral/ignore_oregen + ignore_oregen = TRUE + +/turf/simulated/mineral/floor/ignore_oregen + ignore_oregen = TRUE + +/turf/simulated/mineral/ignore_cavegen + ignore_cavegen = TRUE + +/turf/simulated/mineral/floor/ignore_cavegen + ignore_cavegen = TRUE \ No newline at end of file diff --git a/code/modules/mining/ore_datum_vr.dm b/code/modules/mining/ore_datum_vr.dm new file mode 100644 index 0000000000..e55bc9e599 --- /dev/null +++ b/code/modules/mining/ore_datum_vr.dm @@ -0,0 +1,2 @@ +/ore/coal + compresses_to = "carbon" \ No newline at end of file diff --git a/code/modules/mining/ore_redemption_machine/construction.dm b/code/modules/mining/ore_redemption_machine/construction.dm index dbf8a96ffb..3880ef5cb8 100644 --- a/code/modules/mining/ore_redemption_machine/construction.dm +++ b/code/modules/mining/ore_redemption_machine/construction.dm @@ -10,3 +10,12 @@ req_components = list( /obj/item/weapon/stock_parts/console_screen = 1, /obj/item/weapon/stock_parts/matter_bin = 3) + +/obj/item/weapon/circuitboard/exploration_equipment_vendor + name = T_BOARD("Exploration Equipment Vendor") + board_type = new /datum/frame/frame_types/machine + build_path = /obj/machinery/mineral/equipment_vendor/survey + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 2) + req_components = list( + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/matter_bin = 3) diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 40386f2b71..849550bf6b 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -10,7 +10,7 @@ circuit = /obj/item/weapon/circuitboard/mining_equipment_vendor var/icon_deny = "mining-deny" var/obj/item/weapon/card/id/inserted_id - //VOREStation Edit - Heavily modified list + //VOREStation Edit Start - Heavily modified list var/list/prize_list = list( new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 10), new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100), @@ -20,11 +20,14 @@ new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/smokable/cigarette/cigar/havana, 150), new /datum/data/mining_equipment("Soap", /obj/item/weapon/soap/nanotrasen, 200), new /datum/data/mining_equipment("Laser Pointer", /obj/item/device/laser_pointer, 900), + new /datum/data/mining_equipment("Geiger Counter", /obj/item/device/geiger, 750), new /datum/data/mining_equipment("Plush Toy", /obj/random/plushie, 300), + new /datum/data/mining_equipment("GPS Device", /obj/item/device/gps/mining, 100), // TODO new /datum/data/mining_equipment("Advanced Scanner", /obj/item/device/t_scanner/adv_mining_scanner, 800), new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 500), new /datum/data/mining_equipment("Shelter Capsule", /obj/item/device/survivalcapsule, 500), // TODO new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500), + new /datum/data/mining_equipment("Umbrella", /obj/item/weapon/melee/umbrella/random, 200), new /datum/data/mining_equipment("Point Transfer Card", /obj/item/weapon/card/mining_point_card, 500), new /datum/data/mining_equipment("Survival Medipen", /obj/item/weapon/reagent_containers/hypospray/autoinjector/miner, 500), new /datum/data/mining_equipment("Mini-Translocator", /obj/item/device/perfect_tele/one_beacon, 1200), @@ -33,15 +36,21 @@ new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 900), new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1200), new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/weapon/pickaxe/silver, 1200), - //new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffelbag/mining_conscript, 1000), + // new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffelbag/mining_conscript, 1000), new /datum/data/mining_equipment("Space Cash", /obj/item/weapon/spacecash/c100, 1000), - new /datum/data/mining_equipment("Industrial Hardsuit - Control Module", /obj/item/weapon/rig/industrial, 2000), - new /datum/data/mining_equipment("Industrial Hardsuit - Plasma Cutter", /obj/item/rig_module/device/plasmacutter, 800), - new /datum/data/mining_equipment("Industrial Hardsuit - Drill", /obj/item/rig_module/device/drill, 2000), - new /datum/data/mining_equipment("Industrial Hardsuit - Ore Scanner", /obj/item/rig_module/device/orescanner, 1000), - new /datum/data/mining_equipment("Industrial Hardsuit - Material Scanner", /obj/item/rig_module/vision/material, 500), - new /datum/data/mining_equipment("Industrial Hardsuit - Maneuvering Jets", /obj/item/rig_module/maneuvering_jets, 1250), - new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/weapon/pickaxe/diamond, 2000), + new /datum/data/mining_equipment("Hardsuit - Control Module", /obj/item/weapon/rig/industrial/vendor, 2000), + new /datum/data/mining_equipment("Hardsuit - Plasma Cutter", /obj/item/rig_module/device/plasmacutter, 800), + new /datum/data/mining_equipment("Hardsuit - Drill", /obj/item/rig_module/device/drill, 5000), + new /datum/data/mining_equipment("Hardsuit - Ore Scanner", /obj/item/rig_module/device/orescanner, 1000), + new /datum/data/mining_equipment("Hardsuit - Material Scanner", /obj/item/rig_module/vision/material, 500), + new /datum/data/mining_equipment("Hardsuit - Maneuvering Jets", /obj/item/rig_module/maneuvering_jets, 1250), + new /datum/data/mining_equipment("Hardsuit - Intelligence Storage", /obj/item/rig_module/ai_container, 2500), + new /datum/data/mining_equipment("Hardsuit - Smoke Bomb Deployer", /obj/item/rig_module/grenade_launcher/smoke, 2000), + new /datum/data/mining_equipment("Industrial Equipment - Phoron Bore", /obj/item/weapon/gun/magnetic/matfed, 3000), + new /datum/data/mining_equipment("Industrial Equipment - Sheet-Snatcher",/obj/item/weapon/storage/bag/sheetsnatcher, 500), + new /datum/data/mining_equipment("Digital Tablet - Standard", /obj/item/modular_computer/tablet/preset/custom_loadout/standard, 500), + new /datum/data/mining_equipment("Digital Tablet - Advanced", /obj/item/modular_computer/tablet/preset/custom_loadout/advanced, 1000), + // new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/weapon/pickaxe/diamond, 2000), new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500), new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500), new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/device/survivalcapsule/luxury, 3100), @@ -53,7 +62,23 @@ new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000), new /datum/data/mining_equipment("KA Efficiency Increase", /obj/item/borg/upgrade/modkit/efficiency, 1200), new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000), - new /datum/data/mining_equipment("KA Holster", /obj/item/clothing/accessory/holster/waist/kinetic_accelerator, 350) + new /datum/data/mining_equipment("KA Holster", /obj/item/clothing/accessory/holster/waist/kinetic_accelerator, 350), + new /datum/data/mining_equipment("Fine Excavation Kit - Chisels",/obj/item/weapon/storage/excavation, 500), + new /datum/data/mining_equipment("Fine Excavation Kit - Measuring Tape",/obj/item/device/measuring_tape, 125), + new /datum/data/mining_equipment("Fine Excavation Kit - Hand Pick",/obj/item/weapon/pickaxe/hand, 375), + new /datum/data/mining_equipment("Explosive Excavation Kit - Plastic Charge",/obj/item/weapon/plastique/seismic/locked, 1500), + new /datum/data/mining_equipment("Injector (L) - Glucose",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose, 500), + new /datum/data/mining_equipment("Injector (L) - Panacea",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity, 500), + new /datum/data/mining_equipment("Injector (L) - Trauma",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute, 500), + new /datum/data/mining_equipment("Nanopaste Tube", /obj/item/stack/nanopaste, 1000), + new /datum/data/mining_equipment("Defense Equipment - Smoke Bomb",/obj/item/weapon/grenade/smokebomb, 100), + new /datum/data/mining_equipment("Defense Equipment - Razor Drone Deployer",/obj/item/weapon/grenade/spawnergrenade/manhacks/station/locked, 1000), + new /datum/data/mining_equipment("Defense Equipment - Sentry Drone Deployer",/obj/item/weapon/grenade/spawnergrenade/ward, 1500), + new /datum/data/mining_equipment("Defense Equipment - Plasteel Machete", /obj/item/weapon/material/knife/machete, 500), + new /datum/data/mining_equipment("Fishing Net", /obj/item/weapon/material/fishing_net, 500), + new /datum/data/mining_equipment("Titanium Fishing Rod", /obj/item/weapon/material/fishing_rod/modern, 1000), + new /datum/data/mining_equipment("Durasteel Fishing Rod", /obj/item/weapon/material/fishing_rod/modern/strong, 7500), + new /datum/data/mining_equipment("Bar Shelter Capsule", /obj/item/device/survivalcapsule/luxurybar, 10000) ) //VOREStation Edit End diff --git a/code/modules/mining/ore_redemption_machine/mine_point_items.dm b/code/modules/mining/ore_redemption_machine/mine_point_items.dm index da4463842a..a6a90a3bd6 100644 --- a/code/modules/mining/ore_redemption_machine/mine_point_items.dm +++ b/code/modules/mining/ore_redemption_machine/mine_point_items.dm @@ -15,19 +15,33 @@ name = "mining point card" desc = "A small card preloaded with mining points. Swipe your ID card over it to transfer the points, then discard." icon_state = "data" - var/points = 500 + var/mine_points = 500 + var/survey_points = 0 /obj/item/weapon/card/mining_point_card/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/weapon/card/id)) - if(points) - var/obj/item/weapon/card/id/C = I - C.mining_points += points - to_chat(user, "You transfer [points] points to [C].") - points = 0 + var/obj/item/weapon/card/id/C = I + if(mine_points) + C.mining_points += mine_points + to_chat(user, "You transfer [mine_points] excavation points to [C].") + mine_points = 0 else - to_chat(user, "There's no points left on [src].") + to_chat(user, "There's no excavation points left on [src].") + + if(survey_points) + C.survey_points += survey_points + to_chat(user, "You transfer [survey_points] survey points to [C].") + survey_points = 0 + else + to_chat(user, "There's no survey points left on [src].") + ..() /obj/item/weapon/card/mining_point_card/examine(mob/user) ..(user) - to_chat(user, "There's [points] points on the card.") + to_chat(user, "There's [mine_points] excavation points on the card.") + to_chat(user, "There's [survey_points] survey points on the card.") + +/obj/item/weapon/card/mining_point_card/survey + mine_points = 0 + survey_points = 50 diff --git a/code/modules/mining/ore_redemption_machine/survey_vendor.dm b/code/modules/mining/ore_redemption_machine/survey_vendor.dm new file mode 100644 index 0000000000..b58b343b04 --- /dev/null +++ b/code/modules/mining/ore_redemption_machine/survey_vendor.dm @@ -0,0 +1,111 @@ +/obj/machinery/mineral/equipment_vendor/survey + name = "exploration equipment vendor" + desc = "An equipment vendor for explorers, points collected with a survey scanner can be spent here." + icon = 'icons/obj/machines/mining_machines_vr.dmi' //VOREStation Edit + icon_state = "exploration" //VOREStation Edit + density = TRUE + anchored = TRUE + circuit = /obj/item/weapon/circuitboard/exploration_equipment_vendor + icon_deny = "exploration-deny" //VOREStation Edit + var/icon_vend = "exploration-vend" //VOREStation Add + //VOREStation Edit Start - Heavily modified list + prize_list = list( + new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 1), + new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 10), + new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 30), + new /datum/data/mining_equipment("GPS Device", /obj/item/device/gps/explorer, 10), + new /datum/data/mining_equipment("Whiskey", /obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey, 10), + new /datum/data/mining_equipment("Absinthe", /obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe, 10), + new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/smokable/cigarette/cigar/havana, 15), + new /datum/data/mining_equipment("Soap", /obj/item/weapon/soap/nanotrasen, 20), + new /datum/data/mining_equipment("Laser Pointer", /obj/item/device/laser_pointer, 90), + new /datum/data/mining_equipment("Geiger Counter", /obj/item/device/geiger, 75), + new /datum/data/mining_equipment("Plush Toy", /obj/random/plushie, 30), + new /datum/data/mining_equipment("Extraction Equipment - Fulton Beacon", /obj/item/fulton_core, 300), + new /datum/data/mining_equipment("Extraction Equipment - Fulton Pack", /obj/item/extraction_pack, 125), + new /datum/data/mining_equipment("Umbrella", /obj/item/weapon/melee/umbrella/random, 20), + new /datum/data/mining_equipment("Shelter Capsule", /obj/item/device/survivalcapsule, 50), + new /datum/data/mining_equipment("Point Transfer Card", /obj/item/weapon/card/mining_point_card/survey, 50), + new /datum/data/mining_equipment("Survival Medipen", /obj/item/weapon/reagent_containers/hypospray/autoinjector/miner, 50), + new /datum/data/mining_equipment("Injector (L) - Glucose",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose, 50), + new /datum/data/mining_equipment("Injector (L) - Panacea",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity, 50), + new /datum/data/mining_equipment("Injector (L) - Trauma",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute, 50), + new /datum/data/mining_equipment("Digital Tablet - Standard", /obj/item/modular_computer/tablet/preset/custom_loadout/standard, 50), + new /datum/data/mining_equipment("Digital Tablet - Advanced", /obj/item/modular_computer/tablet/preset/custom_loadout/advanced, 100), + new /datum/data/mining_equipment("Nanopaste Tube", /obj/item/stack/nanopaste, 100), + new /datum/data/mining_equipment("Mini-Translocator", /obj/item/device/perfect_tele/one_beacon, 120), + new /datum/data/mining_equipment("Space Cash", /obj/item/weapon/spacecash/c100, 100), + new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 250), + new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/device/survivalcapsule/luxury, 310), + new /datum/data/mining_equipment("Industrial Equipment - Phoron Bore", /obj/item/weapon/gun/magnetic/matfed, 300), + new /datum/data/mining_equipment("Survey Tools - Shovel", /obj/item/weapon/shovel, 40), + new /datum/data/mining_equipment("Survey Tools - Mechanical Trap", /obj/item/weapon/beartrap, 50), + new /datum/data/mining_equipment("Defense Equipment - Smoke Bomb",/obj/item/weapon/grenade/smokebomb, 10), + new /datum/data/mining_equipment("Defense Equipment - Razor Drone Deployer",/obj/item/weapon/grenade/spawnergrenade/manhacks/station/locked, 100), + new /datum/data/mining_equipment("Defense Equipment - Sentry Drone Deployer",/obj/item/weapon/grenade/spawnergrenade/ward, 150), + new /datum/data/mining_equipment("Defense Equipment - Steel Machete", /obj/item/weapon/material/knife/machete, 75), + new /datum/data/mining_equipment("Fishing Net", /obj/item/weapon/material/fishing_net, 50), + new /datum/data/mining_equipment("Titanium Fishing Rod", /obj/item/weapon/material/fishing_rod/modern, 100), + new /datum/data/mining_equipment("Durasteel Fishing Rod", /obj/item/weapon/material/fishing_rod/modern/strong, 750), + new /datum/data/mining_equipment("Bar Shelter Capsule", /obj/item/device/survivalcapsule/luxurybar, 1000) + ) + //VOREStation Edit End + +/obj/machinery/mineral/equipment_vendor/survey/interact(mob/user) + user.set_machine(src) + + var/dat + dat +="
      " + if(istype(inserted_id)) + dat += "You have [inserted_id.survey_points] survey points collected. Eject ID.
      " + else + dat += "No ID inserted. Insert ID.
      " + dat += "
      " + dat += "
      Equipment point cost list:
      [current_species.blurb][current_species.blurb]

      See the wiki for more details.
      [current_species.blurb]" if("preview" in icon_states(current_species.icobase)) usr << browse_rsc(icon(current_species.icobase,"preview"), "species_preview_[current_species.name].png") @@ -868,12 +899,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O dat += "You cannot play as this species.
      If you wish to be whitelisted, you can make an application post on the forums.

      " else if(restricted == 2) dat += "You cannot play as this species.
      This species is not available for play as a station race..

      " - //VOREStation Addition begin - else if(restricted == 3) - dat += "You cannot play as this species.
      You can however select it and set it up in case admin approves spawning you in.

      " - restricted = 0 - //VOREStation Addition end - if(!restricted || check_rights(R_ADMIN, 0)) + if(!restricted || check_rights(R_ADMIN, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) //VOREStation Edit: selectability dat += "\[select\]" dat += "" diff --git a/code/modules/client/preference_setup/general/06_flavor.dm b/code/modules/client/preference_setup/general/06_flavor.dm index 452752cee8..f03281d0d6 100644 --- a/code/modules/client/preference_setup/general/06_flavor.dm +++ b/code/modules/client/preference_setup/general/06_flavor.dm @@ -59,7 +59,7 @@ switch(href_list["flavor_text"]) if("open") if("general") - var/msg = sanitize(input(usr,"Give a general description of your character. This will be shown regardless of clothing, and may include OOC notes and preferences.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0) + var/msg = sanitize(input(usr,"Give a general description of your character. This will be shown regardless of clothings.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0) //VOREStation Edit: separating out OOC notes if(CanUseTopic(user)) pref.flavor_texts[href_list["flavor_text"]] = msg else diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm index e8b1a56d62..71af8715a5 100644 --- a/code/modules/client/preference_setup/loadout/gear_tweaks.dm +++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm @@ -140,3 +140,302 @@ else . = valid_reagents[metadata] I.reagents.add_reagent(., I.reagents.get_free_space()) + + +/datum/gear_tweak/tablet + var/list/ValidProcessors = list(/obj/item/weapon/computer_hardware/processor_unit/small) + var/list/ValidBatteries = list(/obj/item/weapon/computer_hardware/battery_module/nano, /obj/item/weapon/computer_hardware/battery_module/micro, /obj/item/weapon/computer_hardware/battery_module) + var/list/ValidHardDrives = list(/obj/item/weapon/computer_hardware/hard_drive/micro, /obj/item/weapon/computer_hardware/hard_drive/small, /obj/item/weapon/computer_hardware/hard_drive) + var/list/ValidNetworkCards = list(/obj/item/weapon/computer_hardware/network_card, /obj/item/weapon/computer_hardware/network_card/advanced) + var/list/ValidNanoPrinters = list(null, /obj/item/weapon/computer_hardware/nano_printer) + var/list/ValidCardSlots = list(null, /obj/item/weapon/computer_hardware/card_slot) + var/list/ValidTeslaLinks = list(null, /obj/item/weapon/computer_hardware/tesla_link) + +/datum/gear_tweak/tablet/get_contents(var/list/metadata) + var/list/names = list() + var/obj/O = ValidProcessors[metadata[1]] + if(O) + names += initial(O.name) + O = ValidBatteries[metadata[2]] + if(O) + names += initial(O.name) + O = ValidHardDrives[metadata[3]] + if(O) + names += initial(O.name) + O = ValidNetworkCards[metadata[4]] + if(O) + names += initial(O.name) + O = ValidNanoPrinters[metadata[5]] + if(O) + names += initial(O.name) + O = ValidCardSlots[metadata[6]] + if(O) + names += initial(O.name) + O = ValidTeslaLinks[metadata[7]] + if(O) + names += initial(O.name) + return "[english_list(names, and_text = ", ")]" + +/datum/gear_tweak/tablet/get_metadata(var/user, var/metadata) + . = list() + + var/list/names = list() + var/counter = 1 + for(var/i in ValidProcessors) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + var/entry = input(user, "Choose a processor.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidBatteries) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a battery.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidHardDrives) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a hard drive.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidNetworkCards) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a network card.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidNanoPrinters) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a nanoprinter.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidCardSlots) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a card slot.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidTeslaLinks) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a tesla link.", "Character Preference") in names + . += names[entry] + +/datum/gear_tweak/tablet/get_default() + return list(1, 1, 1, 1, 1, 1, 1) + +/datum/gear_tweak/tablet/tweak_item(var/obj/item/modular_computer/tablet/I, var/list/metadata) + if(ValidProcessors[metadata[1]]) + var/t = ValidProcessors[metadata[1]] + I.processor_unit = new t(I) + if(ValidBatteries[metadata[2]]) + var/t = ValidBatteries[metadata[2]] + I.battery_module = new t(I) + I.battery_module.charge_to_full() + if(ValidHardDrives[metadata[3]]) + var/t = ValidHardDrives[metadata[3]] + I.hard_drive = new t(I) + if(ValidNetworkCards[metadata[4]]) + var/t = ValidNetworkCards[metadata[4]] + I.network_card = new t(I) + if(ValidNanoPrinters[metadata[5]]) + var/t = ValidNanoPrinters[metadata[5]] + I.nano_printer = new t(I) + if(ValidCardSlots[metadata[6]]) + var/t = ValidCardSlots[metadata[6]] + I.card_slot = new t(I) + if(ValidTeslaLinks[metadata[7]]) + var/t = ValidTeslaLinks[metadata[7]] + I.tesla_link = new t(I) + I.update_verbs() + +/datum/gear_tweak/laptop + var/list/ValidProcessors = list(/obj/item/weapon/computer_hardware/processor_unit/small, /obj/item/weapon/computer_hardware/processor_unit) + var/list/ValidBatteries = list(/obj/item/weapon/computer_hardware/battery_module, /obj/item/weapon/computer_hardware/battery_module/advanced, /obj/item/weapon/computer_hardware/battery_module/super) + var/list/ValidHardDrives = list(/obj/item/weapon/computer_hardware/hard_drive, /obj/item/weapon/computer_hardware/hard_drive/advanced, /obj/item/weapon/computer_hardware/hard_drive/super) + var/list/ValidNetworkCards = list(/obj/item/weapon/computer_hardware/network_card, /obj/item/weapon/computer_hardware/network_card/advanced) + var/list/ValidNanoPrinters = list(null, /obj/item/weapon/computer_hardware/nano_printer) + var/list/ValidCardSlots = list(null, /obj/item/weapon/computer_hardware/card_slot) + var/list/ValidTeslaLinks = list(null, /obj/item/weapon/computer_hardware/tesla_link) + +/datum/gear_tweak/laptop/get_contents(var/list/metadata) + var/list/names = list() + var/obj/O = ValidProcessors[metadata[1]] + if(O) + names += initial(O.name) + O = ValidBatteries[metadata[2]] + if(O) + names += initial(O.name) + O = ValidHardDrives[metadata[3]] + if(O) + names += initial(O.name) + O = ValidNetworkCards[metadata[4]] + if(O) + names += initial(O.name) + O = ValidNanoPrinters[metadata[5]] + if(O) + names += initial(O.name) + O = ValidCardSlots[metadata[6]] + if(O) + names += initial(O.name) + O = ValidTeslaLinks[metadata[7]] + if(O) + names += initial(O.name) + return "[english_list(names, and_text = ", ")]" + +/datum/gear_tweak/laptop/get_metadata(var/user, var/metadata) + . = list() + + var/list/names = list() + var/counter = 1 + for(var/i in ValidProcessors) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + var/entry = input(user, "Choose a processor.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidBatteries) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a battery.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidHardDrives) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a hard drive.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidNetworkCards) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a network card.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidNanoPrinters) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a nanoprinter.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidCardSlots) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a card slot.", "Character Preference") in names + . += names[entry] + + names = list() + counter = 1 + for(var/i in ValidTeslaLinks) + if(i) + var/obj/O = i + names[initial(O.name)] = counter++ + else + names["None"] = counter++ + + entry = input(user, "Choose a tesla link.", "Character Preference") in names + . += names[entry] + +/datum/gear_tweak/laptop/get_default() + return list(1, 1, 1, 1, 1, 1, 1) + +/datum/gear_tweak/laptop/tweak_item(var/obj/item/modular_computer/laptop/preset/I, var/list/metadata) + if(ValidProcessors[metadata[1]]) + var/t = ValidProcessors[metadata[1]] + I.processor_unit = new t(I) + if(ValidBatteries[metadata[2]]) + var/t = ValidBatteries[metadata[2]] + I.battery_module = new t(I) + I.battery_module.charge_to_full() + if(ValidHardDrives[metadata[3]]) + var/t = ValidHardDrives[metadata[3]] + I.hard_drive = new t(I) + if(ValidNetworkCards[metadata[4]]) + var/t = ValidNetworkCards[metadata[4]] + I.network_card = new t(I) + if(ValidNanoPrinters[metadata[5]]) + var/t = ValidNanoPrinters[metadata[5]] + I.nano_printer = new t(I) + if(ValidCardSlots[metadata[6]]) + var/t = ValidCardSlots[metadata[6]] + I.card_slot = new t(I) + if(ValidTeslaLinks[metadata[7]]) + var/t = ValidTeslaLinks[metadata[7]] + I.tesla_link = new t(I) + I.update_verbs() diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index 9ba9156fe1..a5e6024653 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -70,6 +70,10 @@ var/list/gear_datums = list() continue if(max_cost && G.cost > max_cost) continue + if(G.ckeywhitelist && !(preference_mob.ckey in G.ckeywhitelist)) //Vorestation Edit + continue //Vorestation Edit + if(G.character_name && !(preference_mob.client.prefs.real_name in G.character_name)) //Vorestation Edit + continue //Vorestation Edit . += gear_name /datum/category_item/player_setup_item/loadout/sanitize_character() @@ -88,7 +92,7 @@ var/list/gear_datums = list() preference_mob << "You cannot have more than one of the \the [gear_name]" pref.gear -= gear_name else if(!(gear_name in valid_gear_choices())) - preference_mob << "You cannot take \the [gear_name] as you are not whitelisted for the species." + preference_mob << "You cannot take \the [gear_name] as you are not whitelisted for the species or item." //Vorestation Edit pref.gear -= gear_name else var/datum/gear/G = gear_datums[gear_name] @@ -100,6 +104,7 @@ var/list/gear_datums = list() /datum/category_item/player_setup_item/loadout/content() . = list() + var/mob/preference_mob = preference_mob() //Vorestation Edit var/total_cost = 0 if(pref.gear && pref.gear.len) for(var/i = 1; i <= pref.gear.len; i++) @@ -145,6 +150,10 @@ var/list/gear_datums = list() . += "

      [G.display_name][G.cost]
      " + for(var/datum/data/mining_equipment/prize in prize_list) + dat += "" + dat += "
      [prize.equipment_name][prize.cost]Purchase
      " + var/datum/browser/popup = new(user, "miningvendor", "Survey Equipment Vendor", 400, 600) + popup.set_content(dat) + popup.open() + +/obj/machinery/mineral/equipment_vendor/survey/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["choice"]) + if(istype(inserted_id)) + if(href_list["choice"] == "eject") + to_chat(usr, "You eject the ID from [src]'s card slot.") + usr.put_in_hands(inserted_id) + inserted_id = null + else if(href_list["choice"] == "insert") + var/obj/item/weapon/card/id/I = usr.get_active_hand() + if(istype(I) && !inserted_id && usr.unEquip(I)) + I.forceMove(src) + inserted_id = I + interact(usr) + to_chat(usr, "You insert the ID into [src]'s card slot.") + else + to_chat(usr, "No valid ID.") + flick(icon_deny, src) + + if(href_list["purchase"]) + if(istype(inserted_id)) + var/datum/data/mining_equipment/prize = locate(href_list["purchase"]) + if (!prize || !(prize in prize_list)) + to_chat(usr, "Error: Invalid choice!") + flick(icon_deny, src) + return + if(prize.cost > inserted_id.survey_points) + to_chat(usr, "Error: Insufficent points for [prize.equipment_name]!") + flick(icon_deny, src) + else + inserted_id.survey_points -= prize.cost + to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]!") + flick(icon_vend, src) //VOREStation Add + new prize.equipment_path(drop_location()) + else + to_chat(usr, "Error: Please insert a valid ID!") + flick(icon_deny, src) + updateUsrDialog() diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index ce1ec09189..55734dfb54 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -15,14 +15,12 @@ w_class = ITEMSIZE_TINY var/template_id = "shelter_alpha" var/datum/map_template/shelter/template - var/datum/map_template/shelter/template_roof var/used = FALSE /obj/item/device/survivalcapsule/proc/get_template() if(template) return template = SSmapping.shelter_templates[template_id] - template_roof = SSmapping.shelter_templates[template.roof] if(!template) throw EXCEPTION("Shelter template ([template_id]) not found!") qdel(src) @@ -49,8 +47,6 @@ var/turf/deploy_location = get_turf(src) var/status = template.check_deploy(deploy_location) var/turf/above_location = GetAbove(deploy_location) - if(above_location && status == SHELTER_DEPLOY_ALLOWED) - status = template.check_deploy(above_location) switch(status) //Not allowed due to /area technical reasons @@ -77,9 +73,11 @@ playsound(get_turf(src), 'sound/effects/phasein.ogg', 100, 1) log_and_message_admins("[key_name_admin(usr)] activated a bluespace capsule at [get_area(T)]!") - if(above_location && template_roof) - template_roof.load(above_location, centered = TRUE) + if(above_location) + template.add_roof(above_location) + template.annihilate_plants(deploy_location) template.load(deploy_location, centered = TRUE) + template.update_lighting(deploy_location) qdel(src) /obj/item/device/survivalcapsule/luxury @@ -87,10 +85,21 @@ desc = "An exorbitantly expensive luxury suite programmed into construction nanomachines. There's a license for use printed on the bottom." template_id = "shelter_beta" +/obj/item/device/survivalcapsule/luxurybar + name = "luxury surfluid bar capsule" + desc = "A luxury bar in a capsule. Bartender required and not included. There's a license for use printed on the bottom." + template_id = "shelter_gamma" + +/obj/item/device/survivalcapsule/military + name = "military surfluid shelter capsule" + desc = "A prefabricated firebase in a capsule. Contains basic weapons, building materials, and combat suits. There's a license for use printed on the bottom." + template_id = "shelter_delta" + +//Custom Shelter Capsules /obj/item/device/survivalcapsule/tabiranth name = "silver-trimmed surfluid shelter capsule" desc = "An exorbitantly expensive luxury suite programmed into construction nanomachines. This one is a particularly rare and expensive model. There's a license for use printed on the bottom." - template_id = "shelter_gamma" + template_id = "shelter_phi" //Pod objects //Walls @@ -98,6 +107,9 @@ name = "survival shelter" stripe_color = "#efbc3b" +/turf/simulated/shuttle/wall/voidcraft/survival/hard_corner + hard_corner = 1 + //Doors /obj/machinery/door/airlock/voidcraft/survival_pod name = "survival airlock" diff --git a/code/modules/mining/shelters.dm b/code/modules/mining/shelters.dm index b68606f914..e7f45e3bf6 100644 --- a/code/modules/mining/shelters.dm +++ b/code/modules/mining/shelters.dm @@ -2,15 +2,12 @@ var/shelter_id var/description var/blacklisted_turfs - var/whitelisted_turfs var/banned_areas var/banned_objects - var/roof /datum/map_template/shelter/New() . = ..() - blacklisted_turfs = typecacheof(/turf/unsimulated) - whitelisted_turfs = list() + blacklisted_turfs = typecacheof(list(/turf/unsimulated, /turf/simulated/floor/tiled)) banned_areas = typecacheof(/area/shuttle) banned_objects = list() @@ -22,8 +19,7 @@ return SHELTER_DEPLOY_BAD_AREA var/banned = is_type_in_typecache(T, blacklisted_turfs) - var/permitted = is_type_in_typecache(T, whitelisted_turfs) - if(banned && !permitted) + if(banned || T.density) return SHELTER_DEPLOY_BAD_TURFS for(var/obj/O in T) @@ -31,6 +27,26 @@ return SHELTER_DEPLOY_ANCHORED_OBJECTS return SHELTER_DEPLOY_ALLOWED +/datum/map_template/shelter/proc/add_roof(turf/deploy_location) + var/affected = get_affected_turfs(deploy_location, centered=TRUE) + for(var/turf/T in affected) + if(isopenspace(T)) + T.ChangeTurf(/turf/simulated/shuttle/floor/voidcraft) + +/datum/map_template/shelter/proc/annihilate_plants(turf/deploy_location) + var/deleted_atoms = 0 + var/affected = get_affected_turfs(deploy_location, centered=TRUE) + for(var/turf/T in affected) + for(var/obj/structure/flora/AM in T) + ++deleted_atoms + qdel(AM) + admin_notice("Annihilated [deleted_atoms] plants.", R_DEBUG) + +/datum/map_template/shelter/proc/update_lighting(turf/deploy_location) + var/affected = get_affected_turfs(deploy_location, centered=TRUE) + for(var/turf/T in affected) + T.lighting_build_overlay() + /datum/map_template/shelter/alpha name = "Shelter Alpha" shelter_id = "shelter_alpha" @@ -39,16 +55,6 @@ sleeping area! Order now, and we'll throw in a TINY FAN, \ absolutely free!" mappath = "maps/submaps/shelters/shelter_1.dmm" - roof = "roof_alpha" - -/datum/map_template/shelter/alpha_roof - shelter_id = "roof_alpha" - mappath = "maps/submaps/shelters/shelter_1_roof.dmm" - -/datum/map_template/shelter/alpha/New() - . = ..() - whitelisted_turfs = typecacheof(/turf/simulated/mineral) - banned_objects = list() /datum/map_template/shelter/beta name = "Shelter Beta" @@ -59,29 +65,33 @@ and a deluxe companion to keep you from getting lonely during \ an ash storm." mappath = "maps/submaps/shelters/shelter_2.dmm" - roof = "roof_beta" - -/datum/map_template/shelter/beta_roof - shelter_id = "roof_beta" - mappath = "maps/submaps/shelters/shelter_2_roof.dmm" - -/datum/map_template/shelter/beta/New() - . = ..() - whitelisted_turfs = typecacheof(/turf/simulated/mineral) - banned_objects = list() /datum/map_template/shelter/gamma name = "Shelter Gamma" shelter_id = "shelter_gamma" + description = "A luxury elite bar which holds an entire bar \ + along with two vending machines, tables, and a restroom that \ + also has a sink. This isn't a survival capsule and so you can \ + expect that this won't save you if you're bleeding out to \ + death." + mappath = "maps/submaps/shelters/shelter_3.dmm" + +/datum/map_template/shelter/delta + name = "Shelter Delta" + shelter_id = "shelter_delta" + description = "A small firebase that contains equipment and supplies \ + for roughly a squad of military troops. Large quantities of \ + supplies allow it to hold out for an extended period of time\ + and a built in medical facility allows field treatment to be \ + possible." + mappath = "maps/submaps/shelters/shelter_4.dmm" + +/datum/map_template/shelter/phi + name = "Shelter Phi" + shelter_id = "shelter_phi" description = "An heavily modified variant of the luxury shelter, \ this particular model has extra food, drinks, and other supplies. \ Originally designed for use by colonists on worlds with little to \ to no contact, the expense of these shelters have prevented them \ from seeing common use." - mappath = "maps/submaps/shelters/shelter_3.dmm" - roof = "roof_beta" - -/datum/map_template/shelter/gamma/New() - . = ..() - whitelisted_turfs = typecacheof(/turf/simulated/mineral) - banned_objects = list() + mappath = "maps/submaps/shelters/shelter_a.dmm" diff --git a/code/modules/mob/_modifiers/medical.dm b/code/modules/mob/_modifiers/medical.dm new file mode 100644 index 0000000000..7e150ceb93 --- /dev/null +++ b/code/modules/mob/_modifiers/medical.dm @@ -0,0 +1,52 @@ + +/* + * Modifiers applied by Medical sources. + */ + +/datum/modifier/bloodpump + name = "external blood pumping" + desc = "Your blood flows thanks to the wonderful power of science." + + on_created_text = "You feel alive." + on_expired_text = "You feel.. less alive." + stacks = MODIFIER_STACK_EXTEND + + pulse_set_level = PULSE_NORM + +/datum/modifier/bloodpump/check_if_valid() + ..() + if(holder.stat == DEAD) + src.expire() + +/datum/modifier/bloodpump_corpse + name = "forced blood pumping" + desc = "Your blood flows thanks to the wonderful power of science." + + on_created_text = "You feel alive." + on_expired_text = "You feel.. less alive." + stacks = MODIFIER_STACK_EXTEND + + pulse_set_level = PULSE_SLOW + +/datum/modifier/bloodpump/corpse/check_if_valid() + ..() + if(holder.stat != DEAD) + src.expire() + +/* + * Modifiers caused by chemicals or organs specifically. + */ + +/datum/modifier/cryogelled + name = "cryogelled" + desc = "Your body begins to freeze." + mob_overlay_state = "chilled" + + on_created_text = "You feel like you're going to freeze! It's hard to move." + on_expired_text = "You feel somewhat warmer and more mobile now." + stacks = MODIFIER_STACK_ALLOWED + + slowdown = 0.1 + evasion = -5 + attack_speed_percent = 1.1 + disable_duration_percent = 1.05 diff --git a/code/modules/mob/_modifiers/modifiers.dm b/code/modules/mob/_modifiers/modifiers.dm index 6e738094a1..665ae80617 100644 --- a/code/modules/mob/_modifiers/modifiers.dm +++ b/code/modules/mob/_modifiers/modifiers.dm @@ -42,7 +42,8 @@ var/accuracy // Positive numbers makes hitting things with guns easier, negatives make it harder. var/accuracy_dispersion // Positive numbers make gun firing cover a wider tile range, and therefore more inaccurate. Negatives help negate dispersion penalties. var/metabolism_percent // Adjusts the mob's metabolic rate, which affects reagent processing. Won't affect mobs without reagent processing. - var/icon_scale_percent // Makes the holder's icon get scaled up or down. + var/icon_scale_x_percent // Makes the holder's icon get scaled wider or thinner. + var/icon_scale_y_percent // Makes the holder's icon get scaled taller or shorter. var/attack_speed_percent // Makes the holder's 'attack speed' (click delay) shorter or longer. var/pain_immunity // Makes the holder not care about pain while this is on. Only really useful to human mobs. var/pulse_modifier // Modifier for pulse, will be rounded on application, then added to the normal 'pulse' multiplier which ranges between 0 and 5 normally. Only applied if they're living. @@ -73,7 +74,7 @@ holder.modifiers.Remove(src) if(mob_overlay_state) // We do this after removing ourselves from the list so that the overlay won't remain. holder.update_modifier_visuals() - if(icon_scale_percent) // Correct the scaling. + if(icon_scale_x_percent || icon_scale_y_percent) // Correct the scaling. holder.update_transform() if(client_color) holder.update_client_color() @@ -140,7 +141,7 @@ mod.on_applied() if(mod.mob_overlay_state) update_modifier_visuals() - if(mod.icon_scale_percent) + if(mod.icon_scale_x_percent || mod.icon_scale_y_percent) update_transform() if(mod.client_color) update_client_color() @@ -232,8 +233,11 @@ effects += "Your metabolism is [metabolism_percent > 1.0 ? "faster" : "slower"], \ causing reagents in your body to process, and hunger to occur [multipler_to_percentage(metabolism_percent, TRUE)] [metabolism_percent > 1.0 ? "faster" : "slower"]." - if(!isnull(icon_scale_percent)) - effects += "Your appearance is [multipler_to_percentage(icon_scale_percent, TRUE)] [icon_scale_percent > 1 ? "larger" : "smaller"]." + if(!isnull(icon_scale_x_percent)) + effects += "Your appearance is [multipler_to_percentage(icon_scale_x_percent, TRUE)] [icon_scale_x_percent > 1 ? "wider" : "thinner"]." + + if(!isnull(icon_scale_y_percent)) + effects += "Your appearance is [multipler_to_percentage(icon_scale_y_percent, TRUE)] [icon_scale_y_percent > 1 ? "taller" : "shorter"]." if(!isnull(attack_speed_percent)) effects += "The delay between attacking is [multipler_to_percentage(attack_speed_percent, TRUE)] [disable_duration_percent > 1.0 ? "longer" : "shorter"]." diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm index 64be119d7c..af6eaa952e 100644 --- a/code/modules/mob/_modifiers/modifiers_misc.dm +++ b/code/modules/mob/_modifiers/modifiers_misc.dm @@ -43,7 +43,7 @@ the artifact triggers the rage. /datum/modifier/berserk name = "berserk" desc = "You are filled with an overwhelming rage." - client_color = "#FF0000" // Make everything red! + client_color = "#FF5555" // Make everything red! mob_overlay_state = "berserk" on_created_text = "You feel an intense and overwhelming rage overtake you as you go berserk!" @@ -56,7 +56,8 @@ the artifact triggers the rage. outgoing_melee_damage_percent = 1.5 // 50% more damage from melee. max_health_percent = 1.5 // More health as a buffer, however the holder might fall into crit after this expires if they're mortally wounded. disable_duration_percent = 0.25 // Disables only last 25% as long. - icon_scale_percent = 1.2 // Look scarier. + icon_scale_x_percent = 1.2 // Look scarier. + icon_scale_y_percent = 1.2 pain_immunity = TRUE // Avoid falling over from shock (at least until it expires). // The less good stuff. @@ -181,6 +182,31 @@ the artifact triggers the rage. accuracy_dispersion = 3 // Ditto. evasion = -45 // Too angry to dodge. +// Speedy, but not hasted. +/datum/modifier/sprinting + name = "sprinting" + desc = "You are filled with energy!" + + on_created_text = "You feel a surge of energy!" + on_expired_text = "The energy high dies out." + stacks = MODIFIER_STACK_EXTEND + + slowdown = -1 + disable_duration_percent = 0.8 + +// Speedy, but not berserked. +/datum/modifier/melee_surge + name = "melee surge" + desc = "You are filled with energy!" + + on_created_text = "You feel a surge of energy!" + on_expired_text = "The energy high dies out." + stacks = MODIFIER_STACK_ALLOWED + + attack_speed_percent = 0.8 + outgoing_melee_damage_percent = 1.1 + disable_duration_percent = 0.8 + // Non-cult version of deep wounds. // Surprisingly, more dangerous. /datum/modifier/grievous_wounds @@ -200,7 +226,6 @@ the artifact triggers the rage. - // Ignition, but confined to the modifier system. // This makes it more predictable and thus, easier to balance. /datum/modifier/fire @@ -269,6 +294,20 @@ the artifact triggers the rage. return FALSE return TRUE +/datum/modifier/poisoned/paralysis + desc = "You have poison inside of you. It will cause harm over a long span of time if not cured, and may cause temporary paralysis." + on_created_text = "You feel incredibly weak..." + damage_per_tick = 0.75 + +/datum/modifier/poisoned/paralysis/tick() + ..() + if(prob(5)) + holder.Paralyse(3) + +/datum/modifier/poisoned/paralysis/on_applied() + ..() + holder.Paralyse(4) + // Pulse modifier. /datum/modifier/false_pulse name = "false pulse" @@ -279,3 +318,28 @@ the artifact triggers the rage. stacks = MODIFIER_STACK_EXTEND pulse_set_level = PULSE_NORM + +/datum/modifier/slow_pulse + name = "slow pulse" + desc = "Your blood flows slower." + + on_created_text = "You feel sluggish." + on_expired_text = "You feel energized." + stacks = MODIFIER_STACK_EXTEND + + bleeding_rate_percent = 0.8 + + pulse_modifier = -1 + +// Temperature Normalizer. +/datum/modifier/homeothermic + name = "temperature resistance" + desc = "Your body normalizes to room temperature." + + on_created_text = "You feel comfortable." + on_expired_text = "You feel.. still probably comfortable." + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/homeothermic/tick() + ..() + holder.bodytemperature = round((holder.bodytemperature + T20C) / 2) diff --git a/code/modules/mob/_modifiers/traits.dm b/code/modules/mob/_modifiers/traits.dm index d0b0abfd89..b15ece941e 100644 --- a/code/modules/mob/_modifiers/traits.dm +++ b/code/modules/mob/_modifiers/traits.dm @@ -60,29 +60,67 @@ metabolism_percent = 0.5 incoming_healing_percent = 0.6 -/datum/modifier/trait/larger - name = "Larger" - desc = "Your body is larger than average." +/datum/modifier/trait/taller + name = "Taller" + desc = "Your body is taller than average." + icon_scale_x_percent = 1 + icon_scale_y_percent = 1.09 - icon_scale_percent = 1.1 +/datum/modifier/trait/tall + name = "Tall" + desc = "Your body is a bit taller than average." + icon_scale_x_percent = 1 + icon_scale_y_percent = 1.05 -/datum/modifier/trait/large - name = "Large" - desc = "Your body is a bit larger than average." +/datum/modifier/trait/short + name = "Short" + desc = "Your body is a bit shorter than average." + icon_scale_x_percent = 1 + icon_scale_y_percent = 0.95 - icon_scale_percent = 1.05 -/datum/modifier/trait/small - name = "Small" - desc = "Your body is a bit smaller than average." +/datum/modifier/trait/shorter + name = "Shorter" + desc = "You are shorter than average." + icon_scale_x_percent = 1 + icon_scale_y_percent = 0.915 - icon_scale_percent = 0.95 +/datum/modifier/trait/fat + name = "Overweight" + desc = "You are heavier than average." -/datum/modifier/trait/smaller - name = "Smaller" - desc = "Your body is smaller than average." + metabolism_percent = 1.2 + icon_scale_x_percent = 1.054 + icon_scale_y_percent = 1 + slowdown = 1.1 + max_health_percent = 1.05 - icon_scale_percent = 0.9 +/datum/modifier/trait/obese + name = "Obese" + desc = "You are much heavier than average." + metabolism_percent = 1.4 + icon_scale_x_percent = 1.095 + icon_scale_y_percent = 1 + slowdown = 1.2 + max_health_percent = 1.10 + +/datum/modifier/trait/thin + name = "Thin" + desc = "You are skinnier than average." + metabolism_percent = 0.8 + icon_scale_x_percent = 0.945 + icon_scale_y_percent = 1 + max_health_percent = 0.95 + outgoing_melee_damage_percent = 0.95 + +/datum/modifier/trait/thinner + name = "Very Thin" + desc = "You are much skinnier than average." + metabolism_percent = 0.6 + icon_scale_x_percent = 0.905 + icon_scale_y_percent = 1 + max_health_percent = 0.90 + outgoing_melee_damage_percent = 0.9 /datum/modifier/trait/colorblind_protanopia name = "Protanopia" diff --git a/code/modules/mob/_modifiers/unholy.dm b/code/modules/mob/_modifiers/unholy.dm index 0eadabc83a..730e57691c 100644 --- a/code/modules/mob/_modifiers/unholy.dm +++ b/code/modules/mob/_modifiers/unholy.dm @@ -14,7 +14,8 @@ disable_duration_percent = 0.25 // Disables only last 25% as long. incoming_damage_percent = 0.5 // 50% incoming damage. - icon_scale_percent = 1.2 // Become a bigger target. + icon_scale_x_percent = 1.2 // Become a bigger target. + icon_scale_y_percent = 1.2 pain_immunity = TRUE slowdown = 2 @@ -145,3 +146,55 @@ if(prob(10)) to_chat(H, "It feels as though your body is being torn apart!") L.updatehealth() + +/datum/modifier/gluttonyregeneration + name = "gluttonous regeneration" + desc = "You are filled with an overwhelming hunger." + mob_overlay_state = "electricity" + + on_created_text = "You feel an intense and overwhelming hunger overtake you as your body regenerates!" + on_expired_text = "The blaze of hunger inside you has been snuffed." + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/gluttonyregeneration/can_apply(var/mob/living/L) + if(L.stat == DEAD) + to_chat(L, "You can't be dead to consume.") + return FALSE + + if(!L.is_sentient()) + return FALSE // Drones don't feel anything, not even hunger. + + if(L.has_modifier_of_type(/datum/modifier/berserk_exhaustion)) + to_chat(L, "You recently berserked, so you are too tired to consume.") + return FALSE + + if(!ishuman(L)) // Only humanoids feel hunger. Totally. + return FALSE + + else + var/mob/living/carbon/human/H = L + if(H.species.name == "Diona") + to_chat(L, "You feel strange for a moment, but it passes.") + return FALSE // Happy trees aren't affected by incredible hunger. + + return ..() + +/datum/modifier/gluttonyregeneration/tick() + spawn() + if(ishuman(holder)) + var/mob/living/carbon/human/H = holder + var/starting_nutrition = H.nutrition + H.nutrition = max(0, H.nutrition - 10) + var/healing_amount = starting_nutrition - H.nutrition + if(healing_amount < 0) // If you are eating enough to somehow outpace this, congratulations, you are gluttonous enough to gain a boon. + healing_amount *= -2 + + H.adjustBruteLoss(-healing_amount * 0.25) + + H.adjustFireLoss(-healing_amount * 0.25) + + H.adjustOxyLoss(-healing_amount * 0.25) + + H.adjustToxLoss(-healing_amount * 0.25) + + ..() \ No newline at end of file diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index a3bf0d5465..758c438a25 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -389,17 +389,17 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return (T && T.holy) && (is_manifest || (mind in cult.current_antagonists)) -/mob/observer/dead/verb/jumptomob(target in getmobs()) //Moves the ghost instead of just changing the ghosts's eye -Nodrak +/mob/observer/dead/verb/jumptomob(input in getmobs()) //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" set popup_menu = FALSE //VOREStation Edit - Declutter. if(istype(usr, /mob/observer/dead)) //Make sure they're an observer! - + var/target = getmobs()[input] if (!target)//Make sure we actually have a target return else - var/mob/M = getmobs()[target] //Destination mob + var/mob/M = target //Destination 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. @@ -466,7 +466,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/turf/t = get_turf(src) if(t) - var/rads = radiation_repository.get_rads_at_turf(t) + var/rads = SSradiation.get_rads_at_turf(t) to_chat(src, "Radiation level: [rads ? rads : "0"] Bq.") diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 5408742654..693b2129e1 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -33,7 +33,7 @@ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages if(!say_understands(speaker,language)) if(language) - message = language.scramble(message) + message = language.scramble(message, languages) else message = stars(message) @@ -165,7 +165,7 @@ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages if(!say_understands(speaker,language)) if(language) - message = language.scramble(message) + message = language.scramble(message, languages) else message = stars(message) diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index 25ee7c64be..25a6ed122a 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -104,6 +104,26 @@ var/list/holder_mob_icon_cache = list() /obj/item/weapon/holder/borer origin_tech = list(TECH_BIO = 6) +/obj/item/weapon/holder/leech + color = "#003366" + origin_tech = list(TECH_BIO = 5, TECH_PHORON = 2) + +/obj/item/weapon/holder/fish + attack_verb = list("fished", "disrespected", "smacked", "smackereled") + hitsound = 'sound/effects/slime_squish.ogg' + slot_flags = SLOT_HOLSTER + origin_tech = list(TECH_BIO = 3) + +/obj/item/weapon/holder/fish/afterattack(var/atom/target, var/mob/living/user, proximity) + if(!target) + return + if(!proximity) + return + if(isliving(target)) + var/mob/living/L = target + if(prob(10)) + L.Stun(2) + /obj/item/weapon/holder/attackby(obj/item/weapon/W as obj, mob/user as mob) for(var/mob/M in src.contents) M.attackby(W,user) diff --git a/code/modules/mob/language/generic.dm b/code/modules/mob/language/generic.dm index 5a46a72038..8fa9efcd73 100644 --- a/code/modules/mob/language/generic.dm +++ b/code/modules/mob/language/generic.dm @@ -32,6 +32,7 @@ syllables = list( "vol", "zum", "coo","zoo","bi","do","ooz","ite","og","re","si","ite","ish", "ar","at","on","ee","east","ma","da", "rim") + partial_understanding = list(LANGUAGE_SKRELLIAN = 30, LANGUAGE_SOL_COMMON = 30) //TODO flag certain languages to use the mob-type specific say_quote and then get rid of these. /datum/language/common/get_spoken_verb(var/msg_end) @@ -64,6 +65,7 @@ colour = "terminus" key = "4" // flags = WHITELISTED (VOREstation edit) + // partial_understanding = list(LANGUAGE_SOL_COMMON = 20) (VOREStation Edit: It is a Zorren language now) syllables = list (".a", "spa", "pan", "blaif", "stra", "!u", "!ei", "!am", "by", ".y", "gry", "zbly", "!y", "fl", "sm", "rn", "cpi", "ku", "koi", "pr", "glau", "stu", "ved", "ki", "tsa", "xau", "jbu", "sny", "stro", "nu", "uan", "ju", "!i", "ge", "luk", "an", "ar", "at", "es", "et", "bel", "ki", "jaa", "ch", "ki", "gh", "ll", "uu", "wat") @@ -76,6 +78,7 @@ colour = "rough" key = "3" space_chance = 45 + partial_understanding = list(LANGUAGE_GALCOM = 10, LANGUAGE_TRADEBAND = 20, LANGUAGE_SOL_COMMON = 20) syllables = list ( "gra","ba","ba","breh","bra","rah","dur","ra","ro","gro","go","ber","bar","geh","heh", "gra", "a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", @@ -211,4 +214,4 @@ ) /datum/language/gibberish/can_speak_special(var/mob/speaker) - return TRUE //Anyone can speak gibberish \ No newline at end of file + return TRUE //Anyone can speak gibberish diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 4822f46a16..83f0b754ba 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -19,6 +19,7 @@ var/list/syllables // Used when scrambling text for a non-speaker. var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string var/machine_understands = 1 // Whether machines can parse and understand this language + var/list/partial_understanding // List of languages that can /somehwat/ understand it, format is: name = chance of understanding a word /datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4, syllable_divisor=2) if(!syllables || !syllables.len) @@ -41,8 +42,42 @@ /datum/language var/list/scramble_cache = list() -/datum/language/proc/scramble(var/input) +/datum/language/proc/scramble(var/input, var/list/known_languages) + var/understand_chance = 0 + for(var/datum/language/L in known_languages) + if(partial_understanding && partial_understanding[L.name]) + understand_chance += partial_understanding[L.name] + if(L.partial_understanding && L.partial_understanding[name]) + understand_chance += L.partial_understanding[name] * 0.5 + var/scrambled_text = "" + var/list/words = splittext(input, " ") + for(var/w in words) + if(prob(understand_chance)) + scrambled_text += " [w] " + else + var/nword = scramble_word(w) + var/ending = copytext(scrambled_text, length(scrambled_text)-1) + if(findtext(ending,".")) + nword = capitalize(nword) + else if(findtext(ending,"!")) + nword = capitalize(nword) + else if(findtext(ending,"?")) + nword = capitalize(nword) + scrambled_text += nword + scrambled_text = replacetext(scrambled_text," "," ") + scrambled_text = capitalize(scrambled_text) + scrambled_text = trim(scrambled_text) + var/ending = copytext(scrambled_text, length(scrambled_text)) + if(ending == ".") + scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1) + var/input_ending = copytext(input, length(input)) + if(input_ending in list("!","?",".")) + scrambled_text += input_ending + + return scrambled_text + +/datum/language/proc/scramble_word(var/input) if(!syllables || !syllables.len) return stars(input) @@ -55,7 +90,7 @@ var/input_size = length(input) var/scrambled_text = "" - var/capitalize = 1 + var/capitalize = 0 while(length(scrambled_text) < input_size) var/next = pick(syllables) @@ -70,14 +105,6 @@ else if(chance > 5 && chance <= space_chance) scrambled_text += " " - scrambled_text = trim(scrambled_text) - var/ending = copytext(scrambled_text, length(scrambled_text)) - if(ending == ".") - scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1) - var/input_ending = copytext(input, input_size) - if(input_ending in list("!","?",".")) - scrambled_text += input_ending - // Add it to cache, cutting old entries if the list is too long scramble_cache[input] = scrambled_text if(scramble_cache.len > SCRAMBLE_CACHE_LEN) @@ -137,6 +164,11 @@ if(name != "Noise") // Audible Emotes if(ishuman(speaker)) var/mob/living/carbon/human/H = speaker + if(H.species.has_organ[O_VOICE] && !(flags & SIGNLANG) && !(flags & NONVERBAL)) // Does the species need a voicebox? Is the language even spoken? + var/obj/item/organ/internal/voicebox/vocal = H.internal_organs_by_name[O_VOICE] + if(!vocal || vocal.is_broken() || vocal.mute) + return FALSE + if(src.name in H.species.assisted_langs) . = FALSE var/obj/item/organ/internal/voicebox/vox = locate() in H.internal_organs // Only voiceboxes for now. Maybe someday it'll include other organs, but I'm not that clever diff --git a/code/modules/mob/language/monkey.dm b/code/modules/mob/language/monkey.dm index c6024d346b..782c5bcf24 100644 --- a/code/modules/mob/language/monkey.dm +++ b/code/modules/mob/language/monkey.dm @@ -5,24 +5,28 @@ ask_verb = "chimpers" exclaim_verb = "screeches" key = "6" + syllables = list("ook","eek") machine_understands = 0 /datum/language/skrell/monkey name = "Neaera" desc = "Squik squik squik." key = "8" + syllables = list("hiss","gronk") machine_understands = 0 /datum/language/unathi/monkey name = "Stok" desc = "Hiss hiss hiss." key = "7" + syllables = list("squick","croak") machine_understands = 0 /datum/language/tajaran/monkey name = "Farwa" desc = "Meow meow meow." key = "9" + syllables = list("meow","mew") machine_understands = 0 /datum/language/corgi diff --git a/code/modules/mob/language/station_vr.dm b/code/modules/mob/language/station_vr.dm index e496bb95f6..b1c5cce255 100644 --- a/code/modules/mob/language/station_vr.dm +++ b/code/modules/mob/language/station_vr.dm @@ -94,6 +94,19 @@ machine_understands = FALSE flags = WHITELISTED | HIVEMIND +/datum/language/human/slavic + name = LANGUAGE_SLAVIC + desc = "The official language of the Independent Colonial Confederation of Gilgamesh, originally established in 2122 by the short-lived United Slavic Confederation on Earth." + colour = "solcom" + key = "r" + + syllables = list( + "rus", "zem", "ave", "groz", "ski", "ska", "ven", "konst", "pol", "lin", "svy", + "danya", "da", "mied", "zan", "das", "krem", "myka", "to", "st", "no", "na", "ni", + "ko", "ne", "en", "po", "ra", "li", "on", "byl", "cto", "eni", "ost", "ol", "ego", + "ver", "stv", "pro" + ) + /datum/language/unathi flags = 0 /datum/language/tajaran @@ -116,4 +129,4 @@ /datum/language/unathi/monkey flags = RESTRICTED /datum/language/tajaran/monkey - flags = RESTRICTED \ No newline at end of file + flags = RESTRICTED diff --git a/code/modules/mob/living/bot/SLed209bot.dm b/code/modules/mob/living/bot/SLed209bot.dm new file mode 100644 index 0000000000..7b5bbc76e5 --- /dev/null +++ b/code/modules/mob/living/bot/SLed209bot.dm @@ -0,0 +1,172 @@ +/mob/living/bot/secbot/ed209/slime + name = "SL-ED-209 Security Robot" + desc = "A security robot. He looks less than thrilled." + icon = 'icons/obj/aibots.dmi' + icon_state = "sled2090" + density = 1 + health = 200 + maxHealth = 200 + + is_ranged = 1 + preparing_arrest_sounds = new() + + a_intent = I_HURT + mob_bump_flag = HEAVY + mob_swap_flags = ~HEAVY + mob_push_flags = HEAVY + + used_weapon = /obj/item/weapon/gun/energy/taser/xeno + + stun_strength = 10 + xeno_harm_strength = 9 + req_one_access = list(access_research, access_robotics) + botcard_access = list(access_research, access_robotics, access_xenobiology, access_xenoarch, access_tox, access_tox_storage, access_maint_tunnels) + var/xeno_stun_strength = 6 + +/mob/living/bot/secbot/ed209/slime/update_icons() + if(on && busy) + icon_state = "sled209-c" + else + icon_state = "sled209[on]" + +/mob/living/bot/secbot/ed209/slime/RangedAttack(var/atom/A) + if(last_shot + shot_delay > world.time) + to_chat(src, "You are not ready to fire yet!") + return + + last_shot = world.time + + var/projectile = /obj/item/projectile/beam/stun/xeno + if(emagged) + projectile = /obj/item/projectile/beam/shock + + playsound(loc, emagged ? 'sound/weapons/laser3.ogg' : 'sound/weapons/Taser.ogg', 50, 1) + var/obj/item/projectile/P = new projectile(loc) + + P.firer = src + P.old_style_target(A) + P.fire() + +/mob/living/bot/secbot/ed209/slime/UnarmedAttack(var/mob/living/L, var/proximity) + ..() + + if(istype(L, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/S = L + S.slimebatoned(src, xeno_stun_strength) + +// Assembly + +/obj/item/weapon/secbot_assembly/ed209_assembly/slime + name = "SL-ED-209 assembly" + desc = "Some sort of bizarre assembly." + icon = 'icons/obj/aibots.dmi' + icon_state = "ed209_frame" + item_state = "buildpipe" + created_name = "SL-ED-209 Security Robot" + +/obj/item/weapon/secbot_assembly/ed209_assembly/slime/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) // Here in the event it's added into a PoI or some such. Standard construction relies on the standard ED up until taser. + if(istype(W, /obj/item/weapon/pen)) + var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && src.loc != usr) + return + created_name = t + return + + switch(build_step) + if(0, 1) + if(istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg) || (istype(W, /obj/item/organ/external/leg) && ((W.name == "robotic right leg") || (W.name == "robotic left leg")))) + user.drop_item() + qdel(W) + build_step++ + to_chat(user, "You add the robot leg to [src].") + name = "legs/frame assembly" + if(build_step == 1) + icon_state = "ed209_leg" + else + icon_state = "ed209_legs" + + if(2) + if(istype(W, /obj/item/clothing/suit/storage/vest)) + user.drop_item() + qdel(W) + build_step++ + to_chat(user, "You add the armor to [src].") + name = "vest/legs/frame assembly" + item_state = "ed209_shell" + icon_state = "ed209_shell" + + if(3) + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(WT.remove_fuel(0, user)) + build_step++ + name = "shielded frame assembly" + to_chat(user, "You welded the vest to [src].") + if(4) + if(istype(W, /obj/item/clothing/head/helmet)) + user.drop_item() + qdel(W) + build_step++ + to_chat(user, "You add the helmet to [src].") + name = "covered and shielded frame assembly" + item_state = "ed209_hat" + icon_state = "ed209_hat" + + if(5) + if(isprox(W)) + user.drop_item() + qdel(W) + build_step++ + to_chat(user, "You add the prox sensor to [src].") + name = "covered, shielded and sensored frame assembly" + item_state = "ed209_prox" + icon_state = "ed209_prox" + + if(6) + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if (C.get_amount() < 1) + to_chat(user, "You need one coil of wire to wire [src].") + return + to_chat(user, "You start to wire [src].") + if(do_after(user, 40) && build_step == 6) + if(C.use(1)) + build_step++ + to_chat(user, "You wire the ED-209 assembly.") + name = "wired ED-209 assembly" + return + + if(7) + if(istype(W, /obj/item/weapon/gun/energy/taser/xeno)) + name = "xenotaser SL-ED-209 assembly" + item_state = "sled209_taser" + icon_state = "sled209_taser" + build_step++ + to_chat(user, "You add [W] to [src].") + user.drop_item() + qdel(W) + + if(8) + if(W.is_screwdriver()) + playsound(src, W.usesound, 100, 1) + var/turf/T = get_turf(user) + to_chat(user, "Now attaching the gun to the frame...") + sleep(40) + if(get_turf(user) == T && build_step == 8) + build_step++ + name = "armed [name]" + to_chat(user, "Taser gun attached.") + + if(9) + if(istype(W, /obj/item/weapon/cell)) + build_step++ + to_chat(user, "You complete the ED-209.") + var/turf/T = get_turf(src) + new /mob/living/bot/secbot/ed209/slime(T,created_name,lasercolor) + user.drop_item() + qdel(W) + user.drop_from_inventory(src) + qdel(src) + diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index ff35a795b5..6d89980bae 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -53,6 +53,9 @@ access_scanner.req_access = req_access.Copy() access_scanner.req_one_access = req_one_access.Copy() + if(!using_map.bot_patrolling) + will_patrol = FALSE + // Make sure mapped in units start turned on. /mob/living/bot/Initialize() . = ..() @@ -88,13 +91,13 @@ /mob/living/bot/attackby(var/obj/item/O, var/mob/user) if(O.GetID()) - if(access_scanner.allowed(user) && !open && !emagged) + if(access_scanner.allowed(user) && !open) locked = !locked to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]") attack_hand(user) - else if(emagged) - to_chat(user, "ERROR") + to_chat(user, "ERROR! SYSTEMS COMPROMISED!") + else if(open) to_chat(user, "Please close the access panel before locking it.") else @@ -111,7 +114,15 @@ else if(istype(O, /obj/item/weapon/weldingtool)) if(health < getMaxHealth()) if(open) - health = min(getMaxHealth(), health + 10) + if(getBruteLoss() < 10) + bruteloss = 0 + else + bruteloss = bruteloss - 10 + if(getFireLoss() < 10) + fireloss = 0 + else + fireloss = fireloss - 10 + updatehealth() user.visible_message("[user] repairs [src].","You repair [src].") playsound(src, O.usesound, 50, 1) else @@ -119,6 +130,13 @@ else to_chat(user, "[src] does not need a repair.") return + else if(istype(O, /obj/item/device/assembly/prox_sensor) && emagged) + if(open) + to_chat(user, "You repair the bot's systems.") + emagged = 0 + qdel(O) + else + to_chat(user, "Unable to repair with the maintenance panel closed.") else ..() diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index eb2371c329..b620ca3e1a 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -113,7 +113,8 @@ dat += "Maintenance panel is [open ? "opened" : "closed"]" if(!locked || issilicon(user)) dat += "
      Cleans Blood: [blood ? "Yes" : "No"]
      " - dat += "
      Patrol station: [will_patrol ? "Yes" : "No"]
      " + if(using_map.bot_patrolling) + dat += "
      Patrol station: [will_patrol ? "Yes" : "No"]
      " if(open && !locked) dat += "Odd looking screw twiddled: [screwloose ? "Yes" : "No"]
      " dat += "Weird button pressed: [oddbutton ? "Yes" : "No"]" diff --git a/code/modules/mob/living/bot/ed209bot.dm b/code/modules/mob/living/bot/ed209bot.dm index b8f1c85485..4db5978d0d 100644 --- a/code/modules/mob/living/bot/ed209bot.dm +++ b/code/modules/mob/living/bot/ed209bot.dm @@ -160,12 +160,30 @@ return if(7) - if(istype(W, /obj/item/weapon/gun/energy/taser)) - name = "taser ED-209 assembly" + if(istype(W, /obj/item/weapon/gun/energy/taser/xeno)) + name = "xenotaser SL-ED-209 assembly" + item_state = "sled209_taser" + icon_state = "sled209_taser" build_step++ to_chat(user, "You add [W] to [src].") + user.drop_item() + qdel(W) + var/turf/T = get_turf(src) + var/obj/item/weapon/secbot_assembly/ed209_assembly/slime/S = new /obj/item/weapon/secbot_assembly/ed209_assembly/slime(T) + S.name = name + S.item_state = item_state + S.icon_state = icon_state + S.build_step = build_step + S.created_name = created_name + user.drop_from_inventory(src) + qdel(src) + + else if(istype(W, /obj/item/weapon/gun/energy/taser)) + name = "taser ED-209 assembly" item_state = "ed209_taser" icon_state = "ed209_taser" + build_step++ + to_chat(user, "You add [W] to [src].") user.drop_item() qdel(W) diff --git a/code/modules/mob/living/bot/edCLNbot.dm b/code/modules/mob/living/bot/edCLNbot.dm index 9b84651165..08d7d9ace6 100644 --- a/code/modules/mob/living/bot/edCLNbot.dm +++ b/code/modules/mob/living/bot/edCLNbot.dm @@ -82,7 +82,8 @@ dat += "Maintenance panel is [open ? "opened" : "closed"]" if(!locked || issilicon(user)) dat += "
      Cleans Blood: [blood ? "Yes" : "No"]
      " - dat += "
      Patrol station: [will_patrol ? "Yes" : "No"]
      " + if(using_map.bot_patrolling) + dat += "
      Patrol station: [will_patrol ? "Yes" : "No"]
      " if(open && !locked) dat += "
      Red Switch: [red_switch ? "On" : "Off"]
      " dat += "
      Green Switch: [green_switch ? "On" : "Off"]
      " diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index 1abc85e870..44aaae7ee1 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -10,7 +10,7 @@ icon_state = "farmbot0" health = 50 maxHealth = 50 - req_one_access = list(access_robotics, access_hydroponics) + req_one_access = list(access_robotics, access_hydroponics, access_xenobiology) //TFF 11/7/19 - adds Xenobio access on behalf of Nalarac var/action = "" // Used to update icon var/waters_trays = 1 @@ -297,7 +297,7 @@ if(tray.dead && removes_dead || tray.harvest && collects_produce) return FARMBOT_COLLECT - else if(refills_water && tray.waterlevel < 40 && !tray.reagents.has_reagent("water")) + else if(refills_water && tray.waterlevel < 40 && !tray.reagents.has_reagent("water") && tank.reagents.total_volume > 0) return FARMBOT_WATER else if(uproots_weeds && tray.weedlevel > 3) diff --git a/code/modules/mob/living/bot/medbot.dm b/code/modules/mob/living/bot/medbot.dm index 0e35d6b2b0..f7bac5b82b 100644 --- a/code/modules/mob/living/bot/medbot.dm +++ b/code/modules/mob/living/bot/medbot.dm @@ -204,8 +204,8 @@ else if((href_list["adj_threshold"]) && (!locked || issilicon(usr))) var/adjust_num = text2num(href_list["adj_threshold"]) heal_threshold += adjust_num - if(heal_threshold < 5) - heal_threshold = 5 + if(heal_threshold <= 0) + heal_threshold = 0.1 if(heal_threshold > 75) heal_threshold = 75 @@ -279,6 +279,9 @@ if(!..()) return 0 + if(H.isSynthetic()) // Don't treat FBPs + return 0 + if(H.stat == DEAD) // He's dead, Jim return 0 diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 3b2d8eb11e..1210d41e91 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -1,4 +1,4 @@ -#define SECBOT_WAIT_TIME 5 //number of in-game seconds to wait for someone to surrender +#define SECBOT_WAIT_TIME 3 //Around number*2 real seconds to surrender. #define SECBOT_THREAT_ARREST 4 //threat level at which we decide to arrest someone #define SECBOT_THREAT_ATTACK 8 //threat level at which was assume immediate danger and attack right away @@ -9,18 +9,20 @@ maxHealth = 100 health = 100 req_one_access = list(access_security, access_forensics_lockers) - botcard_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels) + botcard_access = list(access_security, access_sec_doors, access_forensics_lockers, access_maint_tunnels) patrol_speed = 2 target_speed = 3 var/default_icon_state = "secbot" - var/idcheck = 0 // If true, arrests for having weapons without authorization. - var/check_records = 0 // If true, arrests people without a record. - var/check_arrest = 1 // If true, arrests people who are set to arrest. - var/arrest_type = 0 // If true, doesn't handcuff. You monster. - var/declare_arrests = 0 // If true, announces arrests over sechuds. + var/idcheck = FALSE // If true, arrests for having weapons without authorization. + var/check_records = FALSE // If true, arrests people without a record. + var/check_arrest = TRUE // If true, arrests people who are set to arrest. + var/arrest_type = FALSE // If true, doesn't handcuff. You monster. + var/declare_arrests = FALSE // If true, announces arrests over sechuds. + var/threat = 0 // How much of a threat something is. Set upon acquiring a target. + var/attacked = FALSE // If true, gives the bot enough threat assessment to attack immediately. - var/is_ranged = 0 + var/is_ranged = FALSE var/awaiting_surrender = 0 var/can_next_insult = 0 // Uses world.time var/stun_strength = 60 // For humans. @@ -32,20 +34,25 @@ var/list/threat_found_sounds = list('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg') var/list/preparing_arrest_sounds = list('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/bcreep.ogg') var/list/fighting_sounds = list('sound/voice/biamthelaw.ogg', 'sound/voice/bradio.ogg', 'sound/voice/bjustice.ogg') -//VOREStation Add - They don't like being pulled. This is going to fuck with slimesky, but meh. +//VOREStation Add - They don't like being pulled. This is going to fuck with slimesky, but meh. //Screw you. Just screw you and your 'meh' /mob/living/bot/secbot/Life() ..() if(health > 0 && on && pulledby) if(isliving(pulledby)) - var/mob/living/L = pulledby - UnarmedAttack(L) - say("Do not interfere with active law enforcement routines!") - global_announcer.autosay("[src] was interfered with in [get_area(src)], activating defense routines.", "[src]", "Security") + var/pull_allowed = FALSE + for(var/A in req_one_access) + if(A in pulledby.GetAccess()) + pull_allowed = TRUE + if(!pull_allowed) + var/mob/living/L = pulledby + UnarmedAttack(L) + say("Do not interfere with active law enforcement routines!") + global_announcer.autosay("[src] was interfered with in [get_area(src)], activating defense routines.", "[src]", "Security") //VOREStation Add End /mob/living/bot/secbot/beepsky name = "Officer Beepsky" desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey." - will_patrol = 1 + will_patrol = TRUE /mob/living/bot/secbot/slime name = "Slime Securitron" @@ -88,7 +95,8 @@ dat += "Check Arrest Status: [check_arrest ? "Yes" : "No"]
      " dat += "Operating Mode: [arrest_type ? "Detain" : "Arrest"]
      " dat += "Report Arrests: [declare_arrests ? "Yes" : "No"]
      " - dat += "Auto Patrol: [will_patrol ? "On" : "Off"]" + if(using_map.bot_patrolling) + dat += "Auto Patrol: [will_patrol ? "On" : "Off"]" var/datum/browser/popup = new(user, "autosec", "Securitron controls") popup.set_content(jointext(dat,null)) popup.open() @@ -126,18 +134,18 @@ . = ..() if(!emagged) if(user) - user << "\The [src] buzzes and beeps." - emagged = 1 + to_chat(user, "\The [src] buzzes and beeps.") + emagged = TRUE patrol_speed = 3 target_speed = 4 - return 1 + return TRUE else - user << "\The [src] is already corrupt." + to_chat(user, "\The [src] is already corrupt.") /mob/living/bot/secbot/attackby(var/obj/item/O, var/mob/user) var/curhealth = health . = ..() - if(health < curhealth) + if(health < curhealth && on == TRUE) react_to_attack(user) /mob/living/bot/secbot/bullet_act(var/obj/item/projectile/P) @@ -154,18 +162,21 @@ ..() /mob/living/bot/secbot/proc/react_to_attack(mob/attacker) + if(!on) // We don't want it to react if it's off + return + if(!target) playsound(src.loc, pick(threat_found_sounds), 50) global_announcer.autosay("[src] was attacked by a hostile [target_name(attacker)] in [get_area(src)].", "[src]", "Security") target = attacker - awaiting_surrender = INFINITY // Don't try and wait for surrender + attacked = TRUE // Say "freeze!" and demand surrender /mob/living/bot/secbot/proc/demand_surrender(mob/target, var/threat) var/suspect_name = target_name(target) if(declare_arrests) global_announcer.autosay("[src] is [arrest_type ? "detaining" : "arresting"] a level [threat] suspect [suspect_name] in [get_area(src)].", "[src]", "Security") - say("Down on the floor, [suspect_name]! You have [SECBOT_WAIT_TIME] seconds to comply.") + say("Down on the floor, [suspect_name]! You have [SECBOT_WAIT_TIME*2] seconds to comply.") playsound(src.loc, pick(preparing_arrest_sounds), 50) // Register to be told when the target moves GLOB.moved_event.register(target, src, /mob/living/bot/secbot/proc/target_moved) @@ -179,7 +190,8 @@ /mob/living/bot/secbot/resetTarget() ..() GLOB.moved_event.unregister(target, src) - awaiting_surrender = -1 + awaiting_surrender = 0 + attacked = FALSE walk_to(src, 0) /mob/living/bot/secbot/startPatrol() @@ -189,17 +201,18 @@ /mob/living/bot/secbot/confirmTarget(var/atom/A) if(!..()) - return 0 - return (check_threat(A) >= SECBOT_THREAT_ARREST) + return FALSE + check_threat(A) + if(threat >= SECBOT_THREAT_ARREST) + return TRUE /mob/living/bot/secbot/lookForTargets() for(var/mob/living/M in view(src)) if(M.stat == DEAD) continue if(confirmTarget(M)) - var/threat = check_threat(M) target = M - awaiting_surrender = -1 + awaiting_surrender = 0 say("Level [threat] infraction alert!") custom_emote(1, "points at [M.name]!") playsound(src.loc, pick(threat_found_sounds), 50) @@ -207,15 +220,15 @@ /mob/living/bot/secbot/handleAdjacentTarget() var/mob/living/carbon/human/H = target - var/threat = check_threat(target) + check_threat(target) if(awaiting_surrender < SECBOT_WAIT_TIME && istype(H) && !H.lying && threat < SECBOT_THREAT_ATTACK) - if(awaiting_surrender == -1) // On first tick of awaiting... + if(awaiting_surrender == 0) // On first tick of awaiting... demand_surrender(target, threat) ++awaiting_surrender else if(declare_arrests) var/action = arrest_type ? "detaining" : "arresting" - if(istype(target, /mob/living/simple_mob)) + if(!ishuman(target)) action = "fighting" global_announcer.autosay("[src] is [action] a level [threat] [action != "fighting" ? "suspect" : "threat"] [target_name(target)] in [get_area(src)].", "[src]", "Security") UnarmedAttack(target) @@ -224,7 +237,6 @@ /mob/living/bot/secbot/proc/insult(var/mob/living/L) if(can_next_insult > world.time) return - var/threat = check_threat(L) if(threat >= 10) playsound(src.loc, 'sound/voice/binsult.ogg', 75) can_next_insult = world.time + 20 SECONDS @@ -240,47 +252,47 @@ if(!istype(M)) return - if(istype(M, /mob/living/carbon)) - var/mob/living/carbon/C = M - var/cuff = 1 - if(istype(C, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C - if(istype(H.back, /obj/item/weapon/rig) && istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig)) - cuff = 0 - if(!C.lying || C.handcuffed || arrest_type) - cuff = 0 + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/cuff = TRUE + + if(!H.lying || H.handcuffed || arrest_type) + cuff = FALSE if(!cuff) - C.stun_effect_act(0, stun_strength, null) + H.stun_effect_act(0, stun_strength, null) playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - do_attack_animation(C) - busy = 1 + do_attack_animation(H) + busy = TRUE update_icons() spawn(2) - busy = 0 + busy = FALSE update_icons() - visible_message("\The [C] was prodded by \the [src] with a stun baton!") - insult(C) + visible_message("\The [H] was prodded by \the [src] with a stun baton!") + insult(H) else playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) - visible_message("\The [src] is trying to put handcuffs on \the [C]!") - busy = 1 - if(do_mob(src, C, 60)) - if(!C.handcuffed) - C.handcuffed = new /obj/item/weapon/handcuffs(C) - C.update_inv_handcuffed() - busy = 0 - else if(istype(M, /mob/living/simple_mob)) - var/mob/living/simple_mob/S = M - S.adjustBruteLoss(xeno_harm_strength) + visible_message("\The [src] is trying to put handcuffs on \the [H]!") + busy = TRUE + if(do_mob(src, H, 60)) + if(!H.handcuffed) + if(istype(H.back, /obj/item/weapon/rig) && istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig)) + H.handcuffed = new /obj/item/weapon/handcuffs/cable(H) // Better to be cable cuffed than stun-locked + else + H.handcuffed = new /obj/item/weapon/handcuffs(H) + H.update_inv_handcuffed() + busy = FALSE + else if(istype(M, /mob/living)) + var/mob/living/L = M + L.adjustBruteLoss(xeno_harm_strength) do_attack_animation(M) playsound(loc, "swing_hit", 50, 1, -1) - busy = 1 + busy = TRUE update_icons() spawn(2) - busy = 0 + busy = FALSE update_icons() visible_message("\The [M] was beaten by \the [src] with a stun baton!") - insult(S) + insult(L) /mob/living/bot/secbot/slime/UnarmedAttack(var/mob/living/L, var/proximity) ..() @@ -289,8 +301,6 @@ var/mob/living/simple_mob/slime/xenobio/S = L S.slimebatoned(src, xeno_stun_strength) - - /mob/living/bot/secbot/explode() visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) @@ -319,12 +329,15 @@ /mob/living/bot/secbot/proc/check_threat(var/mob/living/M) if(!M || !istype(M) || M.stat == DEAD || src == M) - return 0 + threat = 0 - if(emagged && !M.incapacitated()) //check incapacitated so emagged secbots don't keep attacking the same target forever - return 10 + else if(emagged && !M.incapacitated()) //check incapacitated so emagged secbots don't keep attacking the same target forever + threat = 10 - return M.assess_perp(access_scanner, 0, idcheck, check_records, check_arrest) + else + threat = M.assess_perp(access_scanner, 0, idcheck, check_records, check_arrest) // Set base threat level + if(attacked) + threat += SECBOT_THREAT_ATTACK // Increase enough so we can attack immediately in return //Secbot Construction @@ -367,12 +380,12 @@ if(WT.remove_fuel(0, user)) build_step = 1 overlays += image('icons/obj/aibots.dmi', "hs_hole") - user << "You weld a hole in \the [src]." + to_chat(user, "You weld a hole in \the [src].") else if(isprox(W) && (build_step == 1)) user.drop_item() build_step = 2 - user << "You add \the [W] to [src]." + to_chat(user, "You add \the [W] to [src].") overlays += image('icons/obj/aibots.dmi', "hs_eye") name = "helmet/signaler/prox sensor assembly" qdel(W) @@ -380,14 +393,14 @@ else if((istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm) || (istype(W, /obj/item/organ/external/arm) && ((W.name == "robotic right arm") || (W.name == "robotic left arm")))) && build_step == 2) user.drop_item() build_step = 3 - user << "You add \the [W] to [src]." + to_chat(user, "You add \the [W] to [src].") name = "helmet/signaler/prox sensor/robot arm assembly" overlays += image('icons/obj/aibots.dmi', "hs_arm") qdel(W) else if(istype(W, /obj/item/weapon/melee/baton) && build_step == 3) user.drop_item() - user << "You complete the Securitron! Beep boop." + to_chat(user, "You complete the Securitron! Beep boop.") if(istype(W, /obj/item/weapon/melee/baton/slime)) var/mob/living/bot/secbot/slime/S = new /mob/living/bot/secbot/slime(get_turf(src)) S.name = created_name @@ -401,6 +414,6 @@ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN) if(!t) return - if(!in_range(src, usr) && loc != usr) + if(!in_range(src, user) && loc != user) return - created_name = t + created_name = t \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm new file mode 100644 index 0000000000..431b7c4456 --- /dev/null +++ b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm @@ -0,0 +1,143 @@ +/mob/living/carbon/human/ai_controlled + name = "Nameless Joe" + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + + var/generate_species = SPECIES_HUMAN + var/generate_dead = FALSE + + var/generate_gender = FALSE + var/generate_id_gender = FALSE + + var/to_wear_hair = "Bald" + + var/to_wear_helmet = /obj/item/clothing/head/welding + var/to_wear_glasses = /obj/item/clothing/glasses/threedglasses + var/to_wear_mask = /obj/item/clothing/mask/gas + var/to_wear_l_radio = /obj/item/device/radio/headset + var/to_wear_r_radio = null + var/to_wear_uniform = /obj/item/clothing/under/color/grey + var/to_wear_suit = /obj/item/clothing/suit/armor/material/makeshift/glass + var/to_wear_gloves = /obj/item/clothing/gloves/ring/material/platinum + var/to_wear_shoes = /obj/item/clothing/shoes/galoshes + var/to_wear_belt = /obj/item/weapon/storage/belt/utility/full + var/to_wear_l_pocket = /obj/item/weapon/soap + var/to_wear_r_pocket = /obj/item/device/pda + var/to_wear_back = /obj/item/weapon/storage/backpack + var/to_wear_id_type = /obj/item/weapon/card/id + var/to_wear_id_job = "Assistant" + + var/to_wear_l_hand = null + var/to_wear_r_hand = /obj/item/weapon/melee/baton + +/mob/living/carbon/human/ai_controlled/Initialize() + if(generate_gender) + gender = pick(list(MALE, FEMALE, PLURAL, NEUTER)) + + if(generate_id_gender) + identifying_gender = pick(list(MALE, FEMALE, PLURAL, NEUTER)) + + ..(loc, generate_species) + + h_style = to_wear_hair + + if(to_wear_uniform) + equip_to_slot_or_del(new to_wear_uniform(src), slot_w_uniform) + + if(to_wear_suit) + equip_to_slot_or_del(new to_wear_suit(src), slot_wear_suit) + + if(to_wear_shoes) + equip_to_slot_or_del(new to_wear_shoes(src), slot_shoes) + + if(to_wear_gloves) + equip_to_slot_or_del(new to_wear_gloves(src), slot_gloves) + + if(to_wear_l_radio) + equip_to_slot_or_del(new to_wear_l_radio(src), slot_l_ear) + + if(to_wear_r_radio) + equip_to_slot_or_del(new to_wear_r_radio(src), slot_r_ear) + + if(to_wear_glasses) + equip_to_slot_or_del(new to_wear_glasses(src), slot_glasses) + + if(to_wear_mask) + equip_to_slot_or_del(new to_wear_mask(src), slot_wear_mask) + + if(to_wear_helmet) + equip_to_slot_or_del(new to_wear_helmet(src), slot_head) + + if(to_wear_belt) + equip_to_slot_or_del(new to_wear_belt(src), slot_belt) + + if(to_wear_r_pocket) + equip_to_slot_or_del(new to_wear_r_pocket(src), slot_r_store) + + if(to_wear_l_pocket) + equip_to_slot_or_del(new to_wear_l_pocket(src), slot_l_store) + + if(to_wear_back) + equip_to_slot_or_del(new to_wear_back(src), slot_back) + + if(to_wear_l_hand) + equip_to_slot_or_del(new to_wear_l_hand(src), slot_l_hand) + + if(to_wear_r_hand) + equip_to_slot_or_del(new to_wear_r_hand(src), slot_r_hand) + + if(to_wear_id_type) + var/obj/item/weapon/card/id/W = new to_wear_id_type(src) + W.name = "[real_name]'s ID Card" + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(J.title == to_wear_id_job) + jobdatum = J + break + if(jobdatum) + W.access = jobdatum.get_access() + else + W.access = list() + if(to_wear_id_job) + W.assignment = to_wear_id_job + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + if(generate_dead) + death() + +/* + * Subtypes. + */ + +/mob/living/carbon/human/ai_controlled/replicant + generate_species = SPECIES_REPLICANT_BETA + + generate_gender = TRUE + identifying_gender = NEUTER + + faction = "xeno" + + to_wear_helmet = /obj/item/clothing/head/helmet/dermal + to_wear_glasses = /obj/item/clothing/glasses/goggles + to_wear_mask = /obj/item/clothing/mask/gas/half + to_wear_l_radio = /obj/item/device/radio/headset/headset_rob + to_wear_r_radio = null + to_wear_uniform = /obj/item/clothing/under/color/grey + to_wear_suit = /obj/item/clothing/suit/armor/vest + to_wear_gloves = null + to_wear_shoes = /obj/item/clothing/shoes/boots/combat/changeling + to_wear_belt = /obj/item/weapon/storage/belt/utility/full + to_wear_l_pocket = /obj/item/weapon/grenade/explosive/mini + to_wear_r_pocket = /obj/item/weapon/grenade/explosive/mini + to_wear_back = /obj/item/device/radio/electropack + to_wear_id_type = /obj/item/weapon/card/id + to_wear_id_job = "Experiment" + + to_wear_r_hand = null + +/mob/living/carbon/human/ai_controlled/replicant/Initialize() + ..() + name = species.get_random_name(gender) + add_modifier(/datum/modifier/homeothermic, 0, null) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 8c687e8de6..dad9e0f41d 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -295,6 +295,7 @@ msg += attempt_vr(src,"examine_pickup_size",args) //VOREStation Code msg += attempt_vr(src,"examine_step_size",args) //VOREStation Code msg += attempt_vr(src,"examine_nif",args) //VOREStation Code + msg += attempt_vr(src,"examine_chimera",args) //VOREStation Code if(mSmallsize in mutations) msg += "[T.He] [T.is] very short!
      " diff --git a/code/modules/mob/living/carbon/human/examine_vr.dm b/code/modules/mob/living/carbon/human/examine_vr.dm index fd6a426447..bdd8678b1f 100644 --- a/code/modules/mob/living/carbon/human/examine_vr.dm +++ b/code/modules/mob/living/carbon/human/examine_vr.dm @@ -105,9 +105,11 @@ message = "[t_He] [t_is] extremely hungry. A deep growl occasionally rumbles from [t_his] empty stomach.\n" if(100 to 499) return message //Well that's pretty normal, really. - if(500 to 1199) // Fat. + if(500 to 999) // Fat. message = "[t_He] [t_has] a stuffed belly, bloated fat and round from eating too much.\n" - if(1200 to 1934) // One person fully digested. + if(1000 to 1399) + message = "[t_He] [t_has] a rotund, thick gut. It bulges from their body obscenely, close to sagging under its own weight.\n" + if(1400 to 1934) // One person fully digested. message = "[t_He] [t_is] sporting a large, round, sagging stomach. It's contains at least their body weight worth of glorping slush.\n" if(1935 to 3004) // Two people. message = "[t_He] [t_is] engorged with a huge stomach that sags and wobbles as they move. [t_He] must have consumed at least twice their body weight. It looks incredibly soft.\n" @@ -155,3 +157,40 @@ /mob/living/carbon/human/proc/examine_nif(mob/living/carbon/human/H) if(nif && nif.examine_msg) //If you have one set, anyway. return "[nif.examine_msg]\n" + +/mob/living/carbon/human/proc/examine_chimera(mob/living/carbon/human/H) + var/t_He = "It" //capitalised for use at the start of each line. + var/t_his = "its" + var/t_His = "Its" + var/t_appear = "appears" + var/t_has = "has" + switch(identifying_gender) //Gender is their "real" gender. Identifying_gender is their "chosen" gender. + if(MALE) + t_He = "He" + t_His = "His" + t_his = "his" + if(FEMALE) + t_He = "She" + t_His = "Her" + t_his = "her" + if(PLURAL) + t_He = "They" + t_His = "Their" + t_his = "their" + t_appear = "appear" + t_has = "have" + if(NEUTER) + t_He = "It" + t_His = "Its" + t_his = "its" + if(HERM) + t_He = "Shi" + t_His = "Hir" + t_his = "hir" + if(revive_ready == REVIVING_NOW || revive_ready == REVIVING_DONE) + if(stat == DEAD) + return "[t_His] body is twitching subtly.\n" + else + return "[t_He] [t_appear] to be in some sort of torpor.\n" + if(feral) + return "[t_He] [t_has] a crazed, wild look in [t_his] eyes!\n" \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 906e64a356..98431adbde 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -638,7 +638,7 @@ src << browse(null, "window=flavor_changes") return if("general") - var/msg = sanitize(input(usr,"Update the general description of your character. This will be shown regardless of clothing, and may include OOC notes and preferences.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]])) as message, extra = 0) + var/msg = sanitize(input(usr,"Update the general description of your character. This will be shown regardless of clothing.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]])) as message, extra = 0) //VOREStation Edit: separating out OOC notes flavor_texts[href_list["flavor_change"]] = msg return else @@ -1145,8 +1145,8 @@ if(species.default_language) add_language(species.default_language) - //if(species.icon_scale != 1) //VOREStation Removal - // update_transform() //VOREStation Removal + //if(species.icon_scale_x != 1 || species.icon_scale_y != 1) //VOREStation Removal + // update_transform() //VOREStation Removal if(example) //VOREStation Edit begin if(!(example == src)) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index dadf02f703..1d7dd6d57a 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -8,6 +8,14 @@ return soft_type return src.default_attack // VOREStation Edit - End + if(src.gloves) + var/obj/item/clothing/gloves/G = src.gloves + if(istype(G) && G.special_attack && G.special_attack.is_usable(src, target, hit_zone)) + if(pulling_punches) + var/datum/unarmed_attack/soft_type = G.special_attack.get_sparring_variant() + if(soft_type) + return soft_type + return G.special_attack for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) if(u_attack.is_usable(src, target, hit_zone)) if(pulling_punches) @@ -27,6 +35,8 @@ if(!temp || !temp.is_usable()) H << "You can't use your hand." return + if(H.lying) + return M.break_cloak() ..() @@ -39,39 +49,11 @@ H.do_attack_animation(src) playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) visible_message("[H] reaches for [src], but misses!") - return 0 + return FALSE if(H != src && check_shields(0, null, H, H.zone_sel.selecting, H.name)) H.do_attack_animation(src) - return 0 - - if(istype(H.gloves, /obj/item/clothing/gloves/boxing/hologlove)) - H.do_attack_animation(src) - var/damage = rand(0, 9) - if(!damage) - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[H] has attempted to punch [src]!") - return 0 - var/obj/item/organ/external/affecting = get_organ(ran_zone(H.zone_sel.selecting)) - var/armor_block = run_armor_check(affecting, "melee") - var/armor_soak = get_armor_soak(affecting, "melee") - - if(HULK in H.mutations) - damage += 5 - - playsound(loc, "punch", 25, 1, -1) - - visible_message("[H] has punched [src]!") - - if(armor_soak >= damage) - return - - apply_damage(damage, HALLOSS, affecting, armor_block, armor_soak) - if(damage >= 9) - visible_message("[H] has weakened [src]!") - apply_effect(4, WEAKEN, armor_block) - - return + return FALSE if(istype(M,/mob/living/carbon)) var/mob/living/carbon/C = M @@ -105,7 +87,7 @@ spawn(30) cpr_time = 1 - H.visible_message("\The [H] is trying perform CPR on \the [src]!") + H.visible_message("\The [H] is trying to perform CPR on \the [src]!") if(!do_after(H, 30)) return @@ -120,7 +102,7 @@ else if(!(M == src && apply_pressure(M, M.zone_sel.selecting))) help_shake_act(M) - return 1 + return TRUE if(I_GRAB) if(M == src || anchored) @@ -135,6 +117,7 @@ var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src) if(buckled) M << "You cannot grab [src], [TT.he] is buckled in!" + return if(!G) //the grab will delete itself in New if affecting is anchored return M.put_in_active_hand(G) @@ -146,7 +129,8 @@ //VORESTATION EDIT visible_message("[M] has grabbed [src] [(M.zone_sel.selecting == BP_L_HAND || M.zone_sel.selecting == BP_R_HAND)? "by [(gender==FEMALE)? "her" : ((gender==MALE)? "his": "their")] hands": "passively"]!") //VORESTATION END END - return 1 + + return TRUE if(I_HURT) @@ -172,7 +156,7 @@ if(!affecting || affecting.is_stump()) M << "They are missing that limb!" - return 1 + return TRUE switch(src.a_intent) if(I_HELP) @@ -242,7 +226,10 @@ // See what attack they use var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, hit_zone) if(!attack) - return 0 + return FALSE + + if(attack.unarmed_override(H, src, hit_zone)) + return FALSE H.do_attack_animation(src) if(!attack_message) @@ -255,7 +242,7 @@ add_attack_logs(H,src,"Melee attacked with fists (miss/block)") if(miss_type) - return 0 + return FALSE var/real_damage = rand_damage var/hit_dam_type = attack.damage_type @@ -265,7 +252,7 @@ var/obj/item/clothing/gloves/G = H.gloves real_damage += G.punch_force hit_dam_type = G.punch_damtype - if(H.pulling_punches) //SO IT IS DECREED: PULLING PUNCHES WILL PREVENT THE ACTUAL DAMAGE FROM RINGS AND KNUCKLES, BUT NOT THE ADDED PAIN + if(H.pulling_punches && !attack.sharp && !attack.edge) //SO IT IS DECREED: PULLING PUNCHES WILL PREVENT THE ACTUAL DAMAGE FROM RINGS AND KNUCKLES, BUT NOT THE ADDED PAIN, BUT YOU CAN'T "PULL" A KNIFE hit_dam_type = AGONY real_damage *= damage_multiplier rand_damage *= damage_multiplier @@ -356,7 +343,7 @@ var/armor_soak = get_armor_soak(affecting, armor_type, armor_pen) apply_damage(damage, BRUTE, affecting, armor_block, armor_soak, sharp = a_sharp, edge = a_edge) updatehealth() - return 1 + return TRUE //Used to attack a joint through grabbing /mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone) @@ -367,43 +354,43 @@ break if(!has_grab) - return 0 + return FALSE if(!def_zone) def_zone = user.zone_sel.selecting var/target_zone = check_zone(def_zone) if(!target_zone) - return 0 + return FALSE var/obj/item/organ/external/organ = get_organ(check_zone(target_zone)) if(!organ || organ.dislocated > 0 || organ.dislocated == -1) //don't use is_dislocated() here, that checks parent - return 0 + return FALSE user.visible_message("[user] begins to dislocate [src]'s [organ.joint]!") if(do_after(user, 100)) organ.dislocate(1) src.visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") - return 1 - return 0 + return TRUE + return FALSE //Breaks all grips and pulls that the mob currently has. /mob/living/carbon/human/proc/break_all_grabs(mob/living/carbon/user) - var/success = 0 + var/success = FALSE if(pulling) visible_message("[user] has broken [src]'s grip on [pulling]!") - success = 1 + success = TRUE stop_pulling() if(istype(l_hand, /obj/item/weapon/grab)) var/obj/item/weapon/grab/lgrab = l_hand if(lgrab.affecting) visible_message("[user] has broken [src]'s grip on [lgrab.affecting]!") - success = 1 + success = TRUE spawn(1) qdel(lgrab) if(istype(r_hand, /obj/item/weapon/grab)) var/obj/item/weapon/grab/rgrab = r_hand if(rgrab.affecting) visible_message("[user] has broken [src]'s grip on [rgrab.affecting]!") - success = 1 + success = TRUE spawn(1) qdel(rgrab) return success @@ -418,11 +405,11 @@ /mob/living/carbon/human/proc/apply_pressure(mob/living/user, var/target_zone) var/obj/item/organ/external/organ = get_organ(target_zone) if(!organ || !(organ.status & ORGAN_BLEEDING) || (organ.robotic >= ORGAN_ROBOT)) - return 0 + return FALSE if(organ.applied_pressure) user << "Someone is already applying pressure to [user == src? "your [organ.name]" : "[src]'s [organ.name]"]." - return 0 + return FALSE var/datum/gender/TU = gender_datums[user.get_visible_gender()] @@ -443,4 +430,4 @@ else user.visible_message("\The [user] stops applying pressure to [src]'s [organ.name]!", "You stop applying pressure to [src]'s [organ.name]!") - return 1 + return TRUE diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 28de4d6d93..1f211fe95b 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -1,5 +1,6 @@ //Updates the mob's health from organs and mob damage variables /mob/living/carbon/human/updatehealth() + var/huskmodifier = 1.5 // With 1.5, you need 250 burn instead of 200 to husk a human. if(status_flags & GODMODE) health = getMaxHealth() @@ -17,7 +18,7 @@ health = getMaxHealth() - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute //TODO: fix husking - if( ((getMaxHealth() - total_burn) < config.health_threshold_dead) && stat == DEAD) + if( ((getMaxHealth() - total_burn) < config.health_threshold_dead * huskmodifier) && stat == DEAD) ChangeToHusk() return diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 57cb8d0f58..6bd592aed6 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -201,6 +201,14 @@ emp_act return gear return null +/mob/living/carbon/human/proc/check_mouth_coverage_survival() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/protective_gear = H.get_covering_clothing(FACE) + for(var/obj/item/gear in protective_gear) + if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL) && !(gear.item_flags & ALLOW_SURVIVALFOOD)) + return gear + return null + /mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) if(!shield) continue @@ -469,6 +477,9 @@ emp_act if(temp && !temp.is_usable()) return FALSE // The hand isn't working in the first place + if(!O.catchable) + return FALSE + // Alright, our hand works? Time to try the catching. var/catch_chance = 90 // Default 90% catch rate diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index a730d811b4..5df33d70e2 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -109,4 +109,6 @@ // Used by mobs in virtual reality to point back to the "real" mob the client belongs to. var/mob/living/carbon/human/vr_holder = null // Used by "real" mobs after they leave a VR session - var/mob/living/carbon/human/vr_link = null \ No newline at end of file + var/mob/living/carbon/human/vr_link = null + + var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently. diff --git a/code/modules/mob/living/carbon/human/human_defines_vr.dm b/code/modules/mob/living/carbon/human/human_defines_vr.dm index 787499afe1..1f197d9c28 100644 --- a/code/modules/mob/living/carbon/human/human_defines_vr.dm +++ b/code/modules/mob/living/carbon/human/human_defines_vr.dm @@ -8,6 +8,7 @@ var/vantag_pref = VANTAG_NONE //What's my status? var/impersonate_bodytype //For impersonating a bodytype var/ability_flags = 0 //Shadekin stoof + var/sensorpref = 5 /mob/living/carbon/human/proc/shadekin_get_eye_color() var/datum/species/shadekin/SK = species @@ -56,4 +57,4 @@ return 0 var/new_amount = SK.get_energy(src) + amount - SK.set_energy(src, new_amount) \ No newline at end of file + SK.set_energy(src, new_amount) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 3491cf3e1d..4bb2770175 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,6 +1,6 @@ #define HUMAN_LOWEST_SLOWDOWN -3 -/mob/living/carbon/human/movement_delay() +/mob/living/carbon/human/movement_delay(oldloc, direct) var/tally = 0 @@ -84,7 +84,7 @@ // Turf related slowdown var/turf/T = get_turf(src) - tally += calculate_turf_slowdown(T) + tally += calculate_turf_slowdown(T, direct) // Item related slowdown. var/item_tally = calculate_item_encumbrance() @@ -133,8 +133,11 @@ . += max(I.slowdown, 0) // Similar to above, but for turf slowdown. -/mob/living/carbon/human/proc/calculate_turf_slowdown(turf/T) - if(T && T.movement_cost) +/mob/living/carbon/human/proc/calculate_turf_slowdown(turf/T, direct) + if(!T) + return 0 + + if(T.movement_cost) var/turf_move_cost = T.movement_cost if(istype(T, /turf/simulated/floor/water)) if(species.water_movement) @@ -144,6 +147,7 @@ if(feet.water_speed) turf_move_cost = CLAMP(HUMAN_LOWEST_SLOWDOWN, turf_move_cost + feet.water_speed, 15) . += turf_move_cost + if(istype(T, /turf/simulated/floor/outdoors/snow)) if(species.snow_movement) turf_move_cost = CLAMP(HUMAN_LOWEST_SLOWDOWN, turf_move_cost + species.snow_movement, 15) @@ -153,6 +157,19 @@ turf_move_cost = CLAMP(HUMAN_LOWEST_SLOWDOWN, turf_move_cost + feet.snow_speed, 15) . += turf_move_cost + // Wind makes it easier or harder to move, depending on if you're with or against the wind. + if(T.outdoors && (T.z <= SSplanets.z_to_planet.len)) + var/datum/planet/P = SSplanets.z_to_planet[z] + if(P) + var/datum/weather_holder/WH = P.weather_holder + if(WH && WH.wind_speed) // Is there any wind? + // With the wind. + if(direct & WH.wind_dir) + . = max(. - WH.wind_speed, -1) // Wind speedup is capped to prevent supersonic speeds from a storm. + // Against it. + else if(direct & reverse_dir[WH.wind_dir]) + . += WH.wind_speed + #undef HUMAN_LOWEST_SLOWDOWN /mob/living/carbon/human/Process_Spacemove(var/check_drift = 0) diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index 6c1311fa41..96005d7407 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -86,7 +86,7 @@ var/obj/item/organ/external/E = organs_by_name[limb_tag] if(!E || !E.is_usable()) stance_damage += 2 // let it fail even if just foot&leg - else if (E.is_malfunctioning()) + else if (E.is_malfunctioning() && !(lying || resting)) //malfunctioning only happens intermittently so treat it as a missing limb when it procs stance_damage += 2 if(isturf(loc) && prob(10)) diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index 809a5ef8ad..a3c5e65470 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -286,8 +286,11 @@ nutrition -= 200 for(var/obj/item/organ/I in internal_organs) + if(I.robotic >= ORGAN_ROBOT) // No free robofix. + continue if(I.damage > 0) I.damage = max(I.damage - 30, 0) //Repair functionally half of a dead internal organ. + I.status = 0 // Wipe status, as it's being regenerated from possibly dead. to_chat(src, "You feel a soothing sensation within your [I.name]...") // Replace completely missing limbs. @@ -310,6 +313,15 @@ var/agony_to_apply = round(0.66 * O.max_damage) // 66% of the limb's health is converted into pain. src.apply_damage(agony_to_apply, HALLOSS) + for(var/organtype in species.has_organ) // Replace completely missing internal organs. -After- external ones, so they all should exist. + if(!src.internal_organs_by_name[organtype]) + var/organpath = species.has_organ[organtype] + var/obj/item/organ/Int = new organpath(src, TRUE) + + Int.rejuvenate(TRUE) + + handle_organs() // Update everything + update_icons_body() active_regen = FALSE else diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index daa19eee64..639425dd59 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -934,7 +934,7 @@ overeatduration -= 2 //doubled the unfat rate if(noisy == TRUE && nutrition < 250 && prob(10)) //VOREStation edit for hunger noises. - var/growlsound = pick(hunger_sounds) + var/sound/growlsound = sound(get_sfx("hunger_sounds")) var/growlmultiplier = 100 - (nutrition / 250 * 100) playsound(src, growlsound, vol = growlmultiplier, vary = 1, falloff = 0.1, ignore_walls = TRUE, preference = /datum/client_preference/digestion_noises) // VOREStation Edit End @@ -994,7 +994,7 @@ var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage if(CE_STABLE in chem_effects) brainOxPercent = 0.008 //Halved in effect - if(oxyloss >= 20 && prob(5)) + if(oxyloss >= (getMaxHealth() * 0.3) && prob(5)) // If oxyloss exceeds 30% of your max health, you can take brain damage. adjustBrainLoss(brainOxPercent * oxyloss) if(halloss >= species.total_health) @@ -1327,6 +1327,11 @@ if(found_welder) client.screen |= global_hud.darkMask +/mob/living/carbon/human/reset_view(atom/A) + ..() + if(machine_visual && machine_visual != A) + machine_visual.remove_visual(src) + /mob/living/carbon/human/handle_vision() if(stat == DEAD) sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF @@ -1388,6 +1393,7 @@ if(machine) var/viewflags = machine.check_eye(src) + machine.apply_visual(src) if(viewflags < 0) reset_view(null, 0) else if(viewflags && !looking_elsewhere) @@ -1563,6 +1569,8 @@ var/temp = PULSE_NORM + var/brain_modifier = 1 + var/modifier_shift = 0 var/modifier_set @@ -1594,6 +1602,14 @@ var/obj/item/organ/internal/heart/Pump = internal_organs_by_name[O_HEART] + var/obj/item/organ/internal/brain/Control = internal_organs_by_name[O_BRAIN] + + if(Control) + brain_modifier = Control.get_control_efficiency() + + if(brain_modifier <= 0.7 && brain_modifier >= 0.4) // 70%-40% control, things start going weird as the brain is failing. + brain_modifier = rand(5, 15) / 10 + if(Pump) temp += Pump.standard_pulse_level - PULSE_NORM @@ -1621,7 +1637,7 @@ if(R.id in cheartstopper) //Conditional heart-stoppage if(R.volume >= R.overdose) temp = PULSE_NONE - return temp + return temp * brain_modifier //handles different chems' influence on pulse for(var/datum/reagent/R in reagents.reagent_list) if(R.id in bradycardics) @@ -1636,7 +1652,7 @@ if(R.volume >= R.overdose) temp = PULSE_NONE - return temp + return max(0, round(temp * brain_modifier)) /mob/living/carbon/human/proc/handle_heartbeat() if(pulse == PULSE_NONE) @@ -1796,12 +1812,6 @@ hud_updateflag = 0 -/mob/living/carbon/human/handle_stunned() - if(!can_feel_pain()) - stunned = 0 - return 0 - return ..() - /mob/living/carbon/human/handle_fire() if(..()) return diff --git a/code/modules/mob/living/carbon/human/species/outsider/replicant.dm b/code/modules/mob/living/carbon/human/species/outsider/replicant.dm index 91658a4851..ec13e29908 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/replicant.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/replicant.dm @@ -60,7 +60,9 @@ O_BRAIN = /obj/item/organ/internal/brain/replicant, O_EYES = /obj/item/organ/internal/eyes/replicant, O_AREJECT = /obj/item/organ/internal/immunehub/replicant, - O_VRLINK = /obj/item/organ/internal/brainmirror + O_VRLINK = /obj/item/organ/internal/brainmirror, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) /datum/species/shapeshifter/replicant/alpha @@ -95,7 +97,9 @@ O_AREJECT = /obj/item/organ/internal/immunehub/replicant, O_PLASMA = /obj/item/organ/internal/xenos/plasmavessel/replicant, O_ACID = /obj/item/organ/internal/xenos/acidgland/replicant, - O_VRLINK = /obj/item/organ/internal/brainmirror + O_VRLINK = /obj/item/organ/internal/brainmirror, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) /datum/species/shapeshifter/replicant/beta @@ -119,5 +123,7 @@ O_VENTC = /obj/item/organ/internal/metamorphgland/replicant, O_PLASMA = /obj/item/organ/internal/xenos/plasmavessel/replicant, O_RESIN = /obj/item/organ/internal/xenos/resinspinner/replicant, - O_VRLINK = /obj/item/organ/internal/brainmirror + O_VRLINK = /obj/item/organ/internal/brainmirror, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 95f909950c..3a8c3b4f19 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -32,7 +32,8 @@ var/tail_animation // If set, the icon to obtain tail animation states from. var/tail_hair - var/icon_scale = 1 // Makes the icon larger/smaller. + var/icon_scale_x = 1 // Makes the icon wider/thinner. + var/icon_scale_y = 1 // Makes the icon taller/shorter. var/race_key = 0 // Used for mob icon cache string. var/icon/icon_template // Used for mob icon generation for non-32x32 species. @@ -195,7 +196,9 @@ O_KIDNEYS = /obj/item/organ/internal/kidneys, O_BRAIN = /obj/item/organ/internal/brain, O_APPENDIX = /obj/item/organ/internal/appendix, - O_EYES = /obj/item/organ/internal/eyes + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) var/vision_organ // If set, this organ is required for vision. Defaults to "eyes" if the species has them. var/dispersed_eyes // If set, the species will be affected by flashbangs regardless if they have eyes or not, as they see in large areas. diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm index f231773f86..cc00d70fc7 100644 --- a/code/modules/mob/living/carbon/human/species/species_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_vr.dm @@ -18,6 +18,7 @@ var/wing var/wing_animation var/icobase_wing + var/wikilink = null //link to wiki page for species /datum/species/proc/update_attack_types() unarmed_attacks = list() diff --git a/code/modules/mob/living/carbon/human/species/station/alraune.dm b/code/modules/mob/living/carbon/human/species/station/alraune.dm index e8f503fe4f..a26c15ad46 100644 --- a/code/modules/mob/living/carbon/human/species/station/alraune.dm +++ b/code/modules/mob/living/carbon/human/species/station/alraune.dm @@ -17,7 +17,7 @@ selects_bodytype = TRUE body_temperature = T20C - breath_type = "carbon_dioxide" + breath_type = "oxygen" poison_type = "phoron" exhale_type = "oxygen" @@ -170,7 +170,7 @@ var/failed_inhale = 0 var/failed_exhale = 0 - inhaling = breath.gas[breath_type] + inhaling = breath.gas["carbon_dioxide"] poison = breath.gas[poison_type] exhaling = breath.gas[exhale_type] @@ -194,7 +194,7 @@ H.oxygen_alert = 0 inhaled_gas_used = inhaling/6 - breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards + breath.adjust_gas("carbon_dioxide", -inhaled_gas_used, update = 0) //update afterwards breath.adjust_gas_temp(exhale_type, inhaled_gas_used, H.bodytemperature, update = 0) //update afterwards //Now we handle CO2. diff --git a/code/modules/mob/living/carbon/human/species/station/human_subspecies.dm b/code/modules/mob/living/carbon/human/species/station/human_subspecies.dm index 56b5c36035..cce1255c0b 100644 --- a/code/modules/mob/living/carbon/human/species/station/human_subspecies.dm +++ b/code/modules/mob/living/carbon/human/species/station/human_subspecies.dm @@ -48,8 +48,11 @@ O_VOICE = /obj/item/organ/internal/voicebox, O_LIVER = /obj/item/organ/internal/liver, O_KIDNEYS = /obj/item/organ/internal/kidneys, + O_SPLEEN = /obj/item/organ/internal/spleen/minor, O_BRAIN = /obj/item/organ/internal/brain, - O_EYES = /obj/item/organ/internal/eyes + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE =/obj/item/organ/internal/intestine ) /* diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm index 7bf92177dd..dea6b254fa 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm @@ -47,7 +47,7 @@ var/datum/species/shapeshifter/promethean/prometheans economic_modifier = 3 - //gluttonous = 1 // VOREStation Edit. Redundant feature. + gluttonous = 1 virus_immune = 1 blood_volume = 560 brute_mod = 0.75 @@ -74,7 +74,13 @@ var/datum/species/shapeshifter/promethean/prometheans genders = list(MALE, FEMALE, NEUTER, PLURAL) unarmed_types = list(/datum/unarmed_attack/slime_glomp) - has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime) // Slime core. + + has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, + O_HEART = /obj/item/organ/internal/heart/grey/colormatch/slime, + O_REGBRUTE = /obj/item/organ/internal/regennetwork, + O_REGBURN = /obj/item/organ/internal/regennetwork/burn, + O_REGOXY = /obj/item/organ/internal/regennetwork/oxy, + O_REGTOX = /obj/item/organ/internal/regennetwork/tox) dispersed_eyes = TRUE @@ -125,11 +131,7 @@ var/datum/species/shapeshifter/promethean/prometheans /obj/item/weapon/storage/toolbox/lunchbox/nymph, /obj/item/weapon/storage/toolbox/lunchbox/syndicate)) //Only pick the empty types var/obj/item/weapon/storage/toolbox/lunchbox/L = new boxtype(get_turf(H)) - var/mob/living/simple_mob/animal/passive/mouse/mouse = new (L) - var/obj/item/weapon/holder/holder = new (L) - holder.held_mob = mouse - mouse.forceMove(holder) - holder.sync(mouse) + new /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar(L) if(H.backbag == 1) H.equip_to_slot_or_del(L, slot_r_hand) else @@ -172,15 +174,40 @@ var/datum/species/shapeshifter/promethean/prometheans H.adjustToxLoss(3 * heal_rate) // Tripled because 0.5 is miniscule, and fire_stacks are capped in both directions healing = FALSE + //Prometheans automatically clean every surface they're in contact with every life tick - this includes the floor without shoes. + //They gain nutrition from doing this. var/turf/T = get_turf(H) if(istype(T)) - var/obj/effect/decal/cleanable/C = locate() in T - if(C && !(H.shoes || (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)))) - qdel(C) + if(!(H.shoes || (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)))) + for(var/obj/O in T) + O.clean_blood() + H.nutrition = min(500, max(0, H.nutrition + rand(5, 15))) if (istype(T, /turf/simulated)) var/turf/simulated/S = T + T.clean_blood() S.dirt = 0 + H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + if(H.clean_blood(1)) H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + if(H.r_hand) + if(H.r_hand.clean_blood()) + H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + if(H.l_hand) + if(H.l_hand.clean_blood()) + H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + if(H.head) + if(H.head.clean_blood()) + H.update_inv_head(0) + H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + if(H.wear_suit) + if(H.wear_suit.clean_blood()) + H.update_inv_wear_suit(0) + H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + if(H.w_uniform) + if(H.w_uniform.clean_blood()) + H.update_inv_w_uniform(0) + H.nutrition = max(H.nutrition, min(500, H.nutrition + rand(15, 30))) //VOREStation Edit: Gives nutrition up to a point instead of being capped + //End cleaning code. var/datum/gas_mixture/environment = T.return_air() var/pressure = environment.return_pressure() @@ -201,35 +228,71 @@ var/datum/species/shapeshifter/promethean/prometheans var/nutrition_cost = 0 // The total amount of nutrition drained every tick, when healing var/nutrition_debt = 0 // Holder variable used to store previous damage values prior to healing for use in the nutrition_cost equation. var/starve_mod = 1 // Lowering this lowers healing and increases agony multiplicatively. - if(H.nutrition <= 150) // This is when the icon goes red + + var/strain_negation = 0 // How much agony is being prevented by the + + if(H.nutrition <= 150) // This is when the icon goes red starve_mod = 0.75 if(H.nutrition <= 50) // Severe starvation. Damage repaired beyond this point will cause a stunlock if untreated. starve_mod = 0.5 + var/to_pay = 0 if(regen_brute) nutrition_debt = H.getBruteLoss() H.adjustBruteLoss(-heal_rate * starve_mod) - nutrition_cost += nutrition_debt - H.getBruteLoss() + + to_pay = nutrition_debt - H.getBruteLoss() + + nutrition_cost += to_pay + + var/obj/item/organ/internal/regennetwork/BrReg = H.internal_organs_by_name[O_REGBRUTE] + + if(BrReg) + strain_negation += to_pay * max(0, (1 - BrReg.get_strain_percent())) if(regen_burn) nutrition_debt = H.getFireLoss() H.adjustFireLoss(-heal_rate * starve_mod) - nutrition_cost += nutrition_debt - H.getFireLoss() + + to_pay = nutrition_debt - H.getFireLoss() + + nutrition_cost += to_pay + + var/obj/item/organ/internal/regennetwork/BuReg = H.internal_organs_by_name[O_REGBURN] + + if(BuReg) + strain_negation += to_pay * max(0, (1 - BuReg.get_strain_percent())) if(regen_oxy) nutrition_debt = H.getOxyLoss() H.adjustOxyLoss(-heal_rate * starve_mod) - nutrition_cost += nutrition_debt - H.getOxyLoss() + + to_pay = nutrition_debt - H.getOxyLoss() + + nutrition_cost += to_pay + + var/obj/item/organ/internal/regennetwork/OxReg = H.internal_organs_by_name[O_REGOXY] + + if(OxReg) + strain_negation += to_pay * max(0, (1 - OxReg.get_strain_percent())) if(regen_tox) nutrition_debt = H.getToxLoss() H.adjustToxLoss(-heal_rate * starve_mod) - nutrition_cost += nutrition_debt - H.getToxLoss() + + to_pay = nutrition_debt - H.getToxLoss() + + nutrition_cost += to_pay + + var/obj/item/organ/internal/regennetwork/ToxReg = H.internal_organs_by_name[O_REGTOX] + + if(ToxReg) + strain_negation += to_pay * max(0, (1 - ToxReg.get_strain_percent())) H.nutrition -= (3 * nutrition_cost) //Costs Nutrition when damage is being repaired, corresponding to the amount of damage being repaired. H.nutrition = max(0, H.nutrition) //Ensure it's not below 0. - var/agony_to_apply = ((1 / starve_mod) * nutrition_cost) //Regenerating damage causes minor pain over time. Small injures will be no issue, large ones will cause problems. + var/agony_to_apply = ((1 / starve_mod) * (nutrition_cost - strain_negation)) //Regenerating damage causes minor pain over time, if the organs responsible are nonexistant or too high on strain. Small injures will be no issue, large ones will cause problems. if((starve_mod <= 0.5 && (H.getHalLoss() + agony_to_apply) <= 90) || ((H.getHalLoss() + agony_to_apply) <= 70)) // Will max out at applying halloss at 70, unless they are starving; starvation regeneration will bring them up to a maximum of 120, the same amount of agony a human receives from three taser hits. H.apply_damage(agony_to_apply, HALLOSS) diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm b/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm index 6204f4d5ba..b1aa904421 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm @@ -1,6 +1,9 @@ /datum/species/shapeshifter/promethean min_age = 18 //Required for server rules max_age = 80 + push_flags = ~HEAVY + swap_flags = ~HEAVY + gluttonous = 0 valid_transform_species = list( "Human", "Unathi", "Tajara", "Skrell", "Diona", "Teshari", "Monkey","Sergal", diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index ae012790cc..6e82989780 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -36,6 +36,7 @@ max_n2 = 0 minbodytemp = 0 maxbodytemp = 900 + movement_cooldown = 0 var/mob/living/carbon/human/humanform var/obj/item/organ/internal/nano/refactory/refactory @@ -200,7 +201,7 @@ target.forceMove(vore_selected) to_chat(target,"\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") -/mob/living/simple_mob/protean_blob/attack_hand(var/atom/A) //VORESTATION AI TEMPORARY REMOVAL (Marking this as such even though it was an edit.) +/mob/living/simple_mob/protean_blob/attack_target(var/atom/A) if(refactory && istype(A,/obj/item/stack/material)) var/obj/item/stack/material/S = A var/substance = S.material.name @@ -346,8 +347,7 @@ var/atom/reform_spot = blob.drop_location() //Size update - transform = matrix()*blob.size_multiplier - size_multiplier = blob.size_multiplier + resize(blob.size_multiplier, FALSE) //Move them back where the blob was forceMove(reform_spot) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm index 3b7f99c8e7..4172a4be3b 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm @@ -200,7 +200,7 @@ var/obj/item/stack/material/matstack = held var/substance = matstack.material.name - var/list/edible_materials = list("steel", "plasteel", "diamond", "mhydrogen") //Can't eat all materials, just useful ones. + var/list/edible_materials = list(MAT_STEEL, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_METALHYDROGEN) //Can't eat all materials, just useful ones. var allowed = FALSE for(var/material in edible_materials) if(material == substance) allowed = TRUE @@ -298,14 +298,14 @@ //Sizing up if(cost > 0) - if(refactory.use_stored_material("steel",cost)) + if(refactory.use_stored_material(MAT_STEEL,cost)) user.resize(size_factor) else to_chat(user,"That size change would cost [cost] steel, which you don't have.") //Sizing down (or not at all) else if(cost <= 0) cost = abs(cost) - var/actually_added = refactory.add_stored_material("steel",cost) + var/actually_added = refactory.add_stored_material(MAT_STEEL,cost) user.resize(size_factor) if(actually_added != cost) to_chat(user,"Unfortunately, [cost-actually_added] steel was lost due to lack of storage space.") diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index 58f7ef5284..0d2000fa01 100755 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -1,5 +1,5 @@ #define DAM_SCALE_FACTOR 0.01 -#define METAL_PER_TICK 150 +#define METAL_PER_TICK 100 /datum/species/protean name = SPECIES_PROTEAN name_plural = "Proteans" @@ -17,7 +17,7 @@ flags = NO_SCAN | NO_SLIP | NO_MINOR_CUT | NO_HALLUCINATION | NO_INFECT | NO_PAIN appearance_flags = HAS_SKIN_COLOR | HAS_EYE_COLOR | HAS_HAIR_COLOR | HAS_UNDERWEAR | HAS_LIPS - spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE health_hud_intensity = 2 num_alternate_languages = 3 assisted_langs = list(LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) @@ -30,9 +30,10 @@ blood_volume = 0 min_age = 18 max_age = 200 - brute_mod = 0.2 //Brute isn't very effective, they're made of dust - burn_mod = 2.0 //Burn, however, is + brute_mod = 1 + burn_mod = 1.4 oxy_mod = 0 + item_slowdown_mod = 1.33 cold_level_1 = 280 //Default 260 - Lower is better cold_level_2 = 220 //Default 200 @@ -42,8 +43,7 @@ heat_level_2 = 370 //Default 400 heat_level_3 = 600 //Default 1000 - //Space doesn't bother them - hazard_low_pressure = -1 + hazard_low_pressure = -1 //Space doesn't bother them hazard_high_pressure = 200 //They can cope with slightly higher pressure //Cold/heat does affect them, but it's done in special ways below @@ -56,11 +56,9 @@ body_temperature = 290 - siemens_coefficient = 3 //Very bad zappy times + siemens_coefficient = 1.5 //Very bad zappy times rarity_value = 5 - darksight = 3 // Equivalent to the minor trait - has_organ = list( O_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain/nano, O_ORCH = /obj/item/organ/internal/nano/orchestrator, @@ -98,7 +96,8 @@ /mob/living/carbon/human/proc/shapeshifter_select_gender, /mob/living/carbon/human/proc/shapeshifter_select_wings, /mob/living/carbon/human/proc/shapeshifter_select_tail, - /mob/living/carbon/human/proc/shapeshifter_select_ears + /mob/living/carbon/human/proc/shapeshifter_select_ears, + /mob/living/proc/eat_trash ) var/global/list/abilities = list() @@ -161,7 +160,7 @@ return rgb(80,80,80,230) /datum/species/protean/handle_death(var/mob/living/carbon/human/H) - to_chat(H,"You died as a Protean. Please sit out of the round for at least 30 minutes before respawning, to represent the time it would take to ship a new-you to the station.") + to_chat(H,"You died as a Protean. Please sit out of the round for at least 60 minutes before respawning, to represent the time it would take to ship a new-you to the station.") spawn(1) //This spawn is here so that if the protean_blob calls qdel, it doesn't try to gib the humanform. if(H) H.gib() @@ -175,16 +174,20 @@ if(refactory && !(refactory.status & ORGAN_DEAD)) //MHydrogen adds speeeeeed - if(refactory.get_stored_material("mhydrogen") >= METAL_PER_TICK) + if(refactory.get_stored_material(MAT_METALHYDROGEN) >= METAL_PER_TICK) H.add_modifier(/datum/modifier/protean/mhydrogen, origin = refactory) - //Plasteel adds brute armor - if(refactory.get_stored_material("plasteel") >= METAL_PER_TICK) - H.add_modifier(/datum/modifier/protean/plasteel, origin = refactory) + //Uranium adds brute armor + if(refactory.get_stored_material(MAT_URANIUM) >= METAL_PER_TICK) + H.add_modifier(/datum/modifier/protean/uranium, origin = refactory) - //Diamond adds burn armor - if(refactory.get_stored_material("diamond") >= METAL_PER_TICK) - H.add_modifier(/datum/modifier/protean/diamond, origin = refactory) + //Gold adds burn armor + if(refactory.get_stored_material(MAT_GOLD) >= METAL_PER_TICK) + H.add_modifier(/datum/modifier/protean/gold, origin = refactory) + + //Silver adds darksight + if(refactory.get_stored_material(MAT_SILVER) >= METAL_PER_TICK) + H.add_modifier(/datum/modifier/protean/silver, origin = refactory) return ..() @@ -246,31 +249,43 @@ on_created_text = "You feel yourself accelerate, the metallic hydrogen increasing your speed temporarily." on_expired_text = "Your refactory finishes consuming the metallic hydrogen, and you return to normal speed." - material_name = "mhydrogen" + material_name = MAT_METALHYDROGEN slowdown = -1 -/datum/modifier/protean/plasteel - name = "Protean Effect - Plasteel" - desc = "You're affected by the presence of plasteel." +/datum/modifier/protean/uranium + name = "Protean Effect - Uranium" + desc = "You're affected by the presence of uranium." - on_created_text = "You feel yourself become nearly impervious to physical attacks as plasteel nanites are made." - on_expired_text = "Your refactory finishes consuming the plasteel, and you return to your normal nanites." + on_created_text = "You feel yourself become nearly impervious to physical attacks as uranium is incorporated in your nanites." + on_expired_text = "Your refactory finishes consuming the uranium, and you return to your normal nanites." - material_name = "plasteel" + material_name = MAT_URANIUM - incoming_brute_damage_percent = 0.5 + incoming_brute_damage_percent = 0.8 -/datum/modifier/protean/diamond - name = "Protean Effect - Diamond" - desc = "You're affected by the presence of diamond." +/datum/modifier/protean/gold + name = "Protean Effect - Gold" + desc = "You're affected by the presence of gold." on_created_text = "You feel yourself become more reflective, able to resist heat and fire better for a time." - on_expired_text = "Your refactory finishes consuming the diamond, and you return to your normal nanites." + on_expired_text = "Your refactory finishes consuming the gold, and you return to your normal nanites." - material_name = "diamond" + material_name = MAT_GOLD - incoming_fire_damage_percent = 0.2 + incoming_fire_damage_percent = 0.8 + +/datum/modifier/protean/silver + name = "Protean Effect - Silver" + desc = "You're affected by the presence of silver." + + on_created_text = "Your physical control is improved for a time, making it easier to hit targets, and avoid being hit." + on_expired_text = "Your refactory finishes consuming the silver, and your motor control returns to normal." + + material_name = MAT_SILVER + + accuracy = 30 + evasion = 30 /datum/modifier/protean/steel name = "Protean Effect - Steel" @@ -279,10 +294,9 @@ on_created_text = "You feel new nanites being produced from your stockpile of steel, healing you slowly." on_expired_text = "Your steel supply has either run out, or is no longer needed, and your healing stops." - material_name = "steel" + material_name = MAT_STEEL /datum/modifier/protean/steel/tick() - ..() holder.adjustBruteLoss(-10,include_robo = TRUE) //Looks high, but these ARE modified by species resistances, so this is really 20% of this holder.adjustFireLoss(-1,include_robo = TRUE) //And this is really double this var/mob/living/carbon/human/H = holder diff --git a/code/modules/mob/living/carbon/human/species/station/seromi.dm b/code/modules/mob/living/carbon/human/species/station/seromi.dm index 0d15d19bbc..01e94f8954 100644 --- a/code/modules/mob/living/carbon/human/species/station/seromi.dm +++ b/code/modules/mob/living/carbon/human/species/station/seromi.dm @@ -110,7 +110,9 @@ O_LIVER = /obj/item/organ/internal/liver, O_KIDNEYS = /obj/item/organ/internal/kidneys, O_BRAIN = /obj/item/organ/internal/brain, - O_EYES = /obj/item/organ/internal/eyes + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) unarmed_types = list( diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index 6a58cb2d08..7c02077f4d 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -25,6 +25,20 @@ spawn_flags = SPECIES_CAN_JOIN appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR + has_organ = list( + O_HEART = /obj/item/organ/internal/heart, + O_LUNGS = /obj/item/organ/internal/lungs, + O_VOICE = /obj/item/organ/internal/voicebox, + O_LIVER = /obj/item/organ/internal/liver, + O_KIDNEYS = /obj/item/organ/internal/kidneys, + O_BRAIN = /obj/item/organ/internal/brain, + O_APPENDIX = /obj/item/organ/internal/appendix, + O_SPLEEN = /obj/item/organ/internal/spleen, + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine + ) + inherent_verbs = list( /mob/living/carbon/human/proc/tie_hair) @@ -122,6 +136,8 @@ O_LIVER = /obj/item/organ/internal/liver/unathi, O_BRAIN = /obj/item/organ/internal/brain/unathi, O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach/unathi, + O_INTESTINE = /obj/item/organ/internal/intestine/unathi ) @@ -228,7 +244,9 @@ O_LIVER = /obj/item/organ/internal/liver, O_KIDNEYS = /obj/item/organ/internal/kidneys, O_BRAIN = /obj/item/organ/internal/brain, - O_EYES = /obj/item/organ/internal/eyes + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) /datum/species/tajaran/equip_survival_gear(var/mob/living/carbon/human/H) @@ -384,7 +402,9 @@ O_LIVER = /obj/item/organ/internal/liver, O_KIDNEYS = /obj/item/organ/internal/kidneys, O_BRAIN = /obj/item/organ/internal/brain, - O_EYES = /obj/item/organ/internal/eyes + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE =/obj/item/organ/internal/intestine ) descriptors = list( diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm index 69c461d767..7599d6f6db 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm @@ -41,6 +41,9 @@ blurb = "Some amalgamation of different species from across the universe,with extremely unstable DNA, making them unfit for regular cloners. \ Widely known for their voracious nature and violent tendencies when stressed or left unfed for long periods of time. \ Most, if not all chimeras possess the ability to undergo some type of regeneration process, at the cost of energy." + + wikilink = "https://wiki.vore-station.net/Xenochimera" + catalogue_data = list(/datum/category_item/catalogue/fauna/xenochimera) hazard_low_pressure = -1 //Prevents them from dying normally in space. Special code handled below. @@ -50,7 +53,7 @@ //primitive_form = "Farwa" - spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED //Whitelisted as restricted is broken. + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE//Whitelisted as restricted is broken. flags = NO_SCAN | NO_INFECT //Dying as a chimera is, quite literally, a death sentence. Well, if it wasn't for their revive, that is. appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR @@ -61,7 +64,9 @@ O_LIVER = /obj/item/organ/internal/liver, O_KIDNEYS = /obj/item/organ/internal/kidneys, O_BRAIN = /obj/item/organ/internal/brain, - O_EYES = /obj/item/organ/internal/eyes + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine ) flesh_color = "#AFA59E" @@ -342,6 +347,9 @@ from their mandible lined mouths. They are a recent discovery by Nanotrasen, only being discovered roughly seven years ago. \ Before they were found they built great cities out of their silk, being united and subjugated in warring factions under great “Star Queens” \ Who forced the working class to build huge, towering cities to attempt to reach the stars, which they worship as gems of great spiritual and magical significance." + + wikilink = "https://wiki.vore-station.net/Vasilissans" + catalogue_data = list(/datum/category_item/catalogue/fauna/vasilissan) hazard_low_pressure = 20 //Prevents them from dying normally in space. Special code handled below. @@ -376,3 +384,63 @@ H.eye_blurry = 5 H.shock_stage = min(H.shock_stage + coldshock, 160) //cold hurts and gives them pain messages, eventually weakening and paralysing, but doesn't damage. return + +/datum/species/werebeast + name = SPECIES_WEREBEAST + name_plural = "Werebeasts" + icobase = 'icons/mob/human_races/r_werebeast.dmi' + deform = 'icons/mob/human_races/r_def_werebeast.dmi' + icon_template = 'icons/mob/human_races/r_werebeast.dmi' + tail = "tail" + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws, /datum/unarmed_attack/bite/sharp) + total_health = 200 + brute_mod = 0.85 + burn_mod = 0.85 + metabolic_rate = 2 + item_slowdown_mod = 0.25 + hunger_factor = 0.4 + darksight = 8 + mob_size = MOB_LARGE + num_alternate_languages = 3 + secondary_langs = list(LANGUAGE_CANILUNZT) + name_language = LANGUAGE_CANILUNZT + primitive_form = "Wolpin" + color_mult = 1 + + min_age = 18 + max_age = 200 + + blurb = "Big buff werewolves. These are a limited functionality event species that are not balanced for regular gameplay. Adminspawn only." + + wikilink="N/A" + + catalogue_data = list(/datum/category_item/catalogue/fauna/vulpkanin) + + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE + appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_EYE_COLOR + inherent_verbs = list( + /mob/living/proc/shred_limb, + /mob/living/proc/eat_trash) + + flesh_color = "#AFA59E" + base_color = "#777777" + + heat_discomfort_strings = list( + "Your fur prickles in the heat.", + "You feel uncomfortably warm.", + "Your overheated skin itches." + ) + + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/vr/werebeast), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) diff --git a/code/modules/mob/living/carbon/human/species/station/station_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_vr.dm index 15a65fc7f2..7a273926b4 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_vr.dm @@ -27,6 +27,9 @@ racial tensions which has resulted in more than a number of wars and outright attempts at genocide. Sergals have an incredibly long \ lifespan, but due to their lust for violence, only a handful have ever survived beyond the age of 80, such as the infamous and \ legendary General Rain Silves who is claimed to have lived to 5000." + + wikilink="https://wiki.vore-station.net/Backstory#Sergal" + catalogue_data = list(/datum/category_item/catalogue/fauna/sergal) primitive_form = "Saru" @@ -88,6 +91,9 @@ allies over the next few hundred years. With the help of Skrellean technology, the Akula had their genome modified to be capable of \ surviving in open air for long periods of time. However, Akula even today still require a high humidity environment to avoid drying out \ after a few days, which would make life on an arid world like Virgo-Prime nearly impossible if it were not for Skrellean technology to aid them." + + wikilink="https://wiki.vore-station.net/Backstory#Akula" + catalogue_data = list(/datum/category_item/catalogue/fauna/akula) primitive_form = "Sobaka" @@ -129,6 +135,9 @@ intelligence and very skillful hands that are put use for constructing precision instruments, but tire-out fast when repeatedly working \ over and over again. Consequently, they struggle to make copies of same things. Both genders have a voice that echoes a lot. Their natural \ tone oscillates between tenor and soprano. They are excessively noisy when they quarrel in their native language." + + wikilink="https://wiki.vore-station.net/Backstory#Nevrean" + catalogue_data = list(/datum/category_item/catalogue/fauna/nevrean) primitive_form = "Sparra" @@ -168,6 +177,8 @@ mountainous areas, they have a differing societal structure than the Flatland Zorren having a more feudal social structure, like the Flatland Zorren, \ the Highland Zorren have also only recently been hired by the Trans-Stellar Corporations, but thanks to the different social structure they seem to \ have adjusted better to their new lives. Though similar fox-like beings have been seen they are different than the Zorren." + wikilink="https://wiki.vore-station.net/Zorren" + catalogue_data = list(/datum/category_item/catalogue/fauna/zorren, /datum/category_item/catalogue/fauna/highzorren) @@ -210,6 +221,8 @@ mountainous areas, they have a differing societal structure than the Flatland Zorren having a more feudal social structure, like the Flatland Zorren, \ the Highland Zorren have also only recently been hired by the Trans-Stellar Corporations, but thanks to the different social structure they \ seem to have adjusted better to their new lives. Though similar fox-like beings have been seen they are different than the Zorren." + wikilink="https://wiki.vore-station.net/Zorren" + catalogue_data = list(/datum/category_item/catalogue/fauna/zorren, /datum/category_item/catalogue/fauna/flatzorren) @@ -254,6 +267,9 @@ culture both feared and respected for their scientific breakthroughs. Discovery, loyalty, and utilitarianism dominates their lifestyles \ to the degree it can cause conflict with more rigorous and strict authorities. They speak a guttural language known as 'Canilunzt' \ which has a heavy emphasis on utilizing tail positioning and ear twitches to communicate intent." + + wikilink="https://wiki.vore-station.net/Backstory#Vulpkanin" + catalogue_data = list(/datum/category_item/catalogue/fauna/vulpkanin) primitive_form = "Wolpin" @@ -288,10 +304,11 @@ but there are multiple exceptions. All xenomorph hybrids have had their ability to lay eggs containing facehuggers \ removed if they had the ability to, although hybrids that previously contained this ability is extremely rare." catalogue_data = list(/datum/category_item/catalogue/fauna/xenohybrid) + // No wiki page for xenohybrids at present //primitive_form = "" //None for these guys - spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR blood_color = "#12ff12" @@ -315,6 +332,7 @@ gluttonous = 0 inherent_verbs = list(/mob/living/proc/shred_limb) descriptors = list() + wikilink="https://wiki.vore-station.net/Unathi" /datum/species/tajaran spawn_flags = SPECIES_CAN_JOIN @@ -326,6 +344,7 @@ gluttonous = 0 //Moving this here so I don't have to fix this conflict every time polaris glances at station.dm inherent_verbs = list(/mob/living/proc/shred_limb, /mob/living/carbon/human/proc/lick_wounds) heat_discomfort_level = 295 //Prevents heat discomfort spam at 20c + wikilink="https://wiki.vore-station.net/Tajaran" /datum/species/skrell spawn_flags = SPECIES_CAN_JOIN @@ -335,28 +354,41 @@ min_age = 18 reagent_tag = null assisted_langs = list(LANGUAGE_EAL, LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) + wikilink="https://wiki.vore-station.net/Skrell" /datum/species/zaddat spawn_flags = SPECIES_CAN_JOIN min_age = 18 gluttonous = 0 descriptors = list() + // no wiki link exists for Zaddat yet + +/datum/species/zaddat/equip_survival_gear(var/mob/living/carbon/human/H) + .=..() + var/obj/item/weapon/storage/toolbox/lunchbox/survival/zaddat/L = new(get_turf(H)) + if(H.backbag == 1) + H.equip_to_slot_or_del(L, slot_r_hand) + else + H.equip_to_slot_or_del(L, slot_in_backpack) /datum/species/diona - spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE min_age = 18 + wikilink="https://wiki.vore-station.net/Diona" /datum/species/teshari mob_size = MOB_MEDIUM spawn_flags = SPECIES_CAN_JOIN icobase = 'icons/mob/human_races/r_seromi_vr.dmi' deform = 'icons/mob/human_races/r_seromi_vr.dmi' + icobase_tail = 1 color_mult = 1 min_age = 18 push_flags = ~HEAVY //Allows them to use micro step code. swap_flags = ~HEAVY gluttonous = 0 descriptors = list() + wikilink="https://wiki.vore-station.net/Teshari" inherent_verbs = list( /mob/living/carbon/human/proc/sonar_ping, @@ -367,6 +399,7 @@ /datum/species/shapeshifter/promethean spawn_flags = SPECIES_CAN_JOIN + wikilink="https://wiki.vore-station.net/Promethean" /datum/species/human color_mult = 1 @@ -375,10 +408,11 @@ appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR min_age = 18 base_color = "#EECEB3" + wikilink="https://wiki.vore-station.net/Human" /datum/species/vox gluttonous = 0 - spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE min_age = 18 icobase = 'icons/mob/human_races/r_vox_old.dmi' deform = 'icons/mob/human_races/r_def_vox_old.dmi' @@ -386,6 +420,7 @@ descriptors = list( /datum/mob_descriptor/vox_markings = 0 ) + wikilink="https://wiki.vore-station.net/Vox" datum/species/harpy name = SPECIES_RAPALA @@ -404,21 +439,20 @@ datum/species/harpy min_age = 18 max_age = 80 + base_color = "#EECEB3" + blurb = "An Avian species, coming from a distant planet, the Rapalas are the very proud race.\ Sol researchers have commented on them having a very close resemblance to the mythical race called 'Harpies',\ who are known for having massive winged arms and talons as feet. They've been clocked at speeds of over 35 miler per hour chasing the planet's many fish-like fauna.\ The Rapalan's home-world 'Verita' is a strangely habitable gas giant, while no physical earth exists, there are fertile floating islands orbiting around the planet from past asteroid activity." - catalogue_data = list(/datum/category_item/catalogue/fauna/rapala) - //primitive_form = "Saru" + wikilink="https://wiki.vore-station.net/Backstory#Rapala" + + catalogue_data = list(/datum/category_item/catalogue/fauna/rapala) spawn_flags = SPECIES_CAN_JOIN appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - //flesh_color = "#AFA59E" - //base_color = "#777777" - //tail_hair = "feathers" - //reagent_tag = IS_SERGAL heat_discomfort_strings = list( "Your feathers prickle in the heat.", diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm index 1d2ce59307..e8b9a070ea 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm @@ -51,7 +51,9 @@ O_BRAIN = /obj/item/organ/internal/brain/xeno, O_PLASMA = /obj/item/organ/internal/xenos/plasmavessel, O_HIVE = /obj/item/organ/internal/xenos/hivenode, - O_NUTRIENT = /obj/item/organ/internal/diona/nutrients + O_NUTRIENT = /obj/item/organ/internal/diona/nutrients, + O_STOMACH = /obj/item/organ/internal/stomach/xeno, + O_INTESTINE = /obj/item/organ/internal/intestine/xeno ) bump_flag = ALIEN @@ -182,7 +184,9 @@ O_ACID = /obj/item/organ/internal/xenos/acidgland, O_HIVE = /obj/item/organ/internal/xenos/hivenode, O_RESIN = /obj/item/organ/internal/xenos/resinspinner, - O_NUTRIENT = /obj/item/organ/internal/diona/nutrients + O_NUTRIENT = /obj/item/organ/internal/diona/nutrients, + O_STOMACH = /obj/item/organ/internal/stomach/xeno, + O_INTESTINE = /obj/item/organ/internal/intestine/xeno ) inherent_verbs = list( @@ -218,7 +222,9 @@ O_BRAIN = /obj/item/organ/internal/brain/xeno, O_PLASMA = /obj/item/organ/internal/xenos/plasmavessel/hunter, O_HIVE = /obj/item/organ/internal/xenos/hivenode, - O_NUTRIENT = /obj/item/organ/internal/diona/nutrients + O_NUTRIENT = /obj/item/organ/internal/diona/nutrients, + O_STOMACH = /obj/item/organ/internal/stomach/xeno, + O_INTESTINE = /obj/item/organ/internal/intestine/xeno ) inherent_verbs = list( @@ -247,7 +253,9 @@ O_PLASMA = /obj/item/organ/internal/xenos/plasmavessel/sentinel, O_ACID = /obj/item/organ/internal/xenos/acidgland, O_HIVE = /obj/item/organ/internal/xenos/hivenode, - O_NUTRIENT = /obj/item/organ/internal/diona/nutrients + O_NUTRIENT = /obj/item/organ/internal/diona/nutrients, + O_STOMACH = /obj/item/organ/internal/stomach/xeno, + O_INTESTINE = /obj/item/organ/internal/intestine/xeno ) inherent_verbs = list( @@ -284,7 +292,9 @@ O_ACID = /obj/item/organ/internal/xenos/acidgland, O_HIVE = /obj/item/organ/internal/xenos/hivenode, O_RESIN = /obj/item/organ/internal/xenos/resinspinner, - O_NUTRIENT = /obj/item/organ/internal/diona/nutrients + O_NUTRIENT = /obj/item/organ/internal/diona/nutrients, + O_STOMACH = /obj/item/organ/internal/stomach/xeno, + O_INTESTINE = /obj/item/organ/internal/intestine/xeno ) inherent_verbs = list( diff --git a/code/modules/mob/living/carbon/human/unarmed_attack.dm b/code/modules/mob/living/carbon/human/unarmed_attack.dm index f3526983ef..43a5ae66f2 100644 --- a/code/modules/mob/living/carbon/human/unarmed_attack.dm +++ b/code/modules/mob/living/carbon/human/unarmed_attack.dm @@ -25,18 +25,18 @@ var/global/list/sparring_attack_cache = list() /datum/unarmed_attack/proc/is_usable(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone) if(user.restrained()) - return 0 + return FALSE // Check if they have a functioning hand. var/obj/item/organ/external/E = user.organs_by_name["l_hand"] if(E && !E.is_stump()) - return 1 + return TRUE E = user.organs_by_name["r_hand"] if(E && !E.is_stump()) - return 1 + return TRUE - return 0 + return FALSE /datum/unarmed_attack/proc/get_unarmed_damage() return damage @@ -105,6 +105,9 @@ var/global/list/sparring_attack_cache = list() return user.visible_message("[user] attempts to press [TU.his] [eye_attack_text] into [target]'s eyes, but [TT.he] [TT.does]n't have any!") +/datum/unarmed_attack/proc/unarmed_override(var/mob/living/carbon/human/user,var/mob/living/carbon/human/target,var/zone) + return FALSE //return true if the unarmed override prevents further attacks + /datum/unarmed_attack/bite attack_verb = list("bit") attack_sound = 'sound/weapons/bite.ogg' @@ -121,7 +124,7 @@ var/global/list/sparring_attack_cache = list() return 0 if (user == target && (zone == BP_HEAD || zone == O_EYES || zone == O_MOUTH)) return 0 - return 1 + return TRUE /datum/unarmed_attack/punch attack_verb = list("punched") @@ -187,20 +190,20 @@ var/global/list/sparring_attack_cache = list() /datum/unarmed_attack/kick/is_usable(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone) if (user.legcuffed) - return 0 + return FALSE if(!(zone in list("l_leg", "r_leg", "l_foot", "r_foot", BP_GROIN))) - return 0 + return FALSE var/obj/item/organ/external/E = user.organs_by_name["l_foot"] if(E && !E.is_stump()) - return 1 + return TRUE E = user.organs_by_name["r_foot"] if(E && !E.is_stump()) - return 1 + return TRUE - return 0 + return FALSE /datum/unarmed_attack/kick/get_unarmed_damage(var/mob/living/carbon/human/user) var/obj/item/clothing/shoes = user.shoes @@ -231,23 +234,23 @@ var/global/list/sparring_attack_cache = list() /datum/unarmed_attack/stomp/is_usable(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone) if (user.legcuffed) - return 0 + return FALSE if(!istype(target)) - return 0 + return FALSE if (!user.lying && (target.lying || (zone in list("l_foot", "r_foot")))) if(target.grabbed_by == user && target.lying) - return 0 + return FALSE var/obj/item/organ/external/E = user.organs_by_name["l_foot"] if(E && !E.is_stump()) - return 1 + return TRUE E = user.organs_by_name["r_foot"] if(E && !E.is_stump()) - return 1 + return TRUE - return 0 + return FALSE /datum/unarmed_attack/stomp/get_unarmed_damage(var/mob/living/carbon/human/user) var/obj/item/clothing/shoes = user.shoes diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index f12c5fbe95..de4f0ba0dc 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -124,15 +124,20 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() /mob/living/carbon/human/update_transform() /* VOREStation Edit START - TODO - Consider switching to icon_scale // First, get the correct size. - var/desired_scale = icon_scale + var/desired_scale_x = icon_scale_x + var/desired_scale_y = icon_scale_y - desired_scale *= species.icon_scale + desired_scale_x *= species.icon_scale_x + desired_scale_y *= species.icon_scale_y for(var/datum/modifier/M in modifiers) - if(!isnull(M.icon_scale_percent)) - desired_scale *= M.icon_scale_percent + if(!isnull(M.icon_scale_x_percent)) + desired_scale_x *= M.icon_scale_x_percent + if(!isnull(M.icon_scale_y_percent)) + desired_scale_y *= M.icon_scale_y_percent */ - var/desired_scale = size_multiplier + var/desired_scale_x = size_multiplier + var/desired_scale_y = size_multiplier //VOREStation Edit End // Regular stuff again. @@ -145,12 +150,12 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone. M.Turn(90) - M.Scale(desired_scale) + M.Scale(desired_scale_x, desired_scale_y) M.Translate(1,-6) layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters else - M.Scale(desired_scale) - M.Translate(0, 16*(desired_scale-1)) + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, 16*(desired_scale_y-1)) layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters animate(src, transform = M, time = anim_time) @@ -241,7 +246,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() for(var/organ_tag in species.has_limbs) var/obj/item/organ/external/part = organs_by_name[organ_tag] - if(isnull(part) || part.is_stump()) + if(isnull(part) || part.is_stump() || part.is_hidden_by_tail()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. icon_key += "0" continue if(part) @@ -284,13 +289,13 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() base_icon = chest.get_icon() for(var/obj/item/organ/external/part in organs) - if(isnull(part) || part.is_stump()) + if(isnull(part) || part.is_stump() || part.is_hidden_by_tail()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. continue var/icon/temp = part.get_icon(skeleton) //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) if(part.icon_position & (LEFT | RIGHT)) - var/icon/temp2 = new('icons/mob/human.dmi',"blank") + var/icon/temp2 = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") //VOREStation Edit. temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) if(!(part.icon_position & LEFT)) @@ -435,8 +440,13 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() // VOREStation Edit - START var/icon/ears_s = get_ears_overlay() - if (ears_s) + if(ears_s) face_standing.Blend(ears_s, ICON_OVERLAY) + if(istype(head_organ,/obj/item/organ/external/head/vr)) + var/obj/item/organ/external/head/vr/head_organ_vr = head_organ + overlays_standing[HAIR_LAYER] = image(face_standing, layer = BODY_LAYER+HAIR_LAYER, "pixel_y" = head_organ_vr.head_offset) + apply_layer(HAIR_LAYER) + return // VOREStation Edit - END if(head_organ.nonsolid) @@ -571,7 +581,15 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() return //Wearing a suit that prevents uniform rendering //Build a uniform sprite - overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = INV_W_UNIFORM_DEF_ICON, default_layer = UNIFORM_LAYER) + //VOREStation Edit start. + var/icon/c_mask = null + if(tail_style && tail_style.clip_mask_icon && tail_style.clip_mask_state) + var/obj/item/clothing/suit/S = wear_suit + if(!(wear_suit && ((wear_suit.flags_inv & HIDETAIL) || (istype(S) && S.taurized)))) //Clip the lower half of the suit off using the tail's clip mask. + c_mask = new /icon(tail_style.clip_mask_icon, tail_style.clip_mask_state) + overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = INV_W_UNIFORM_DEF_ICON, default_layer = UNIFORM_LAYER, clip_mask = c_mask) + //VOREStation Edit end. + apply_layer(UNIFORM_LAYER) /mob/living/carbon/human/update_inv_wear_id() @@ -653,6 +671,12 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if(!shoes || (wear_suit && wear_suit.flags_inv & HIDESHOES) || (w_uniform && w_uniform.flags_inv & HIDESHOES)) return //Either nothing to draw, or it'd be hidden. + //VOREStation Edit + for(var/f in list(BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/foot/foot = get_organ(f) + if(istype(foot) && foot.is_hidden_by_tail()) //If either foot is hidden by the tail, don't render footwear. + return + //Allow for shoe layer toggle nonsense var/shoe_layer = SHOES_LAYER if(istype(shoes, /obj/item/clothing/shoes)) @@ -736,12 +760,18 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() // Part of splitting the suit sprites up var/iconFile = INV_SUIT_DEF_ICON + var/obj/item/clothing/suit/S //VOREStation edit - break this var out a level for use below. if(istype(wear_suit, /obj/item/clothing/suit)) - var/obj/item/clothing/suit/S = wear_suit + S = wear_suit if(S.update_icon_define) iconFile = S.update_icon_define - overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = iconFile, default_layer = SUIT_LAYER) + //VOREStation Edit start. + var/icon/c_mask = null + if((tail_style && tail_style.clip_mask_icon && tail_style.clip_mask_state) && !(wear_suit.flags_inv & HIDETAIL) && !(S && S.taurized)) //Clip the lower half of the suit off using the tail's clip mask. + c_mask = new /icon(tail_style.clip_mask_icon, tail_style.clip_mask_state) + overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = iconFile, default_layer = SUIT_LAYER, clip_mask = c_mask) + //VOREStation Edit end. apply_layer(SUIT_LAYER) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 37d9e43b37..55bddfe1bd 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -26,6 +26,9 @@ if(COLD_RESISTANCE in mutations) damage = 0 adjustFireLoss(damage * blocked) + if(SEARING) + apply_damage(damage / 3, BURN, def_zone, blocked, soaked, used_weapon, sharp, edge) + apply_damage(damage / 3 * 2, BRUTE, def_zone, blocked, soaked, used_weapon, sharp, edge) if(TOX) adjustToxLoss(damage * blocked) if(OXY) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 24d56945b8..0b37b89076 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -9,6 +9,10 @@ handle_modifiers() //VOREStation Edit - Needs to be done even if in nullspace. if(!loc) return + + if(machine && !CanMouseDrop(machine, src)) + machine = null + var/datum/gas_mixture/environment = loc.return_air() //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 4020f13d14..ff04da63e0 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1048,6 +1048,14 @@ default behaviour is: else to_chat(src, "You feel nauseous...") + if(ishuman(src)) + var/mob/living/carbon/human/Hu = src + if(CE_ANTACID in Hu.chem_effects) + if(prob(min(90, Hu.chem_effects[CE_ANTACID] * 15))) + spawn(rand(30 SECONDS, 2 MINUTES)) + lastpuke = FALSE + return + spawn() if(!skip_wait) sleep(150) //15 seconds until second warning @@ -1060,7 +1068,7 @@ default behaviour is: var/mob/living/carbon/human/H = src if(!H.isSynthetic()) var/obj/item/organ/internal/liver/L = H.internal_organs_by_name["liver"] - if(L.is_broken()) + if(!L || L.is_broken()) blood_vomit = 1 Stun(5) @@ -1174,19 +1182,20 @@ default behaviour is: /mob/living/update_transform() // First, get the correct size. - var/desired_scale = size_multiplier //VOREStation edit + var/desired_scale_x = size_multiplier //VOREStation edit + var/desired_scale_y = size_multiplier //VOREStation edit for(var/datum/modifier/M in modifiers) - if(!isnull(M.icon_scale_percent)) - desired_scale *= M.icon_scale_percent + if(!isnull(M.icon_scale_x_percent)) + desired_scale_x *= M.icon_scale_x_percent + if(!isnull(M.icon_scale_y_percent)) + desired_scale_y *= M.icon_scale_y_percent // Now for the regular stuff. var/matrix/M = matrix() - M.Scale(desired_scale) - M.Translate(0, 16*(desired_scale-1)) - src.transform = M + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, 16*(desired_scale_y-1)) //animate(src, transform = M, time = 10) //VOREStation edit - // This handles setting the client's color variable, which makes everything look a specific color. // This proc is here so it can be called without needing to check if the client exists, or if the client relogs. /mob/living/update_client_color() @@ -1280,6 +1289,7 @@ default behaviour is: var/turf/end_T = get_turf(target) if(end_T) add_attack_logs(src,M,"Thrown via grab to [end_T.x],[end_T.y],[end_T.z]") + src.drop_from_inventory(G) src.drop_from_inventory(item) if(!item || !isturf(item.loc)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b3ee75349c..6384e90466 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -216,6 +216,9 @@ /mob/living/proc/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] with [I.name] by [user]!") + if(ai_holder) + ai_holder.react_to_attack(user) + var/soaked = get_armor_soak(hit_zone, "melee") var/blocked = run_armor_check(hit_zone, "melee") diff --git a/code/modules/mob/living/living_defines_vr.dm b/code/modules/mob/living/living_defines_vr.dm index 8b463df93b..6e9b96fc13 100644 --- a/code/modules/mob/living/living_defines_vr.dm +++ b/code/modules/mob/living/living_defines_vr.dm @@ -1,3 +1,12 @@ +/mob + var/muffled = 0 // Used by muffling belly + /mob/living var/ooc_notes = null - var/obj/structure/mob_spawner/source_spawner = null \ No newline at end of file + var/obj/structure/mob_spawner/source_spawner = null + +//custom say verbs + var/custom_say = null + var/custom_ask = null + var/custom_exclaim = null + var/custom_whisper = null \ No newline at end of file diff --git a/code/modules/mob/living/living_vr.dm b/code/modules/mob/living/living_vr.dm new file mode 100644 index 0000000000..7616ac61d5 --- /dev/null +++ b/code/modules/mob/living/living_vr.dm @@ -0,0 +1,25 @@ +/mob/living/verb/customsay() + set category = "IC" + set name = "Customise Speech Verbs" + set desc = "Customise the text which appears when you type- e.g. 'says', 'asks', 'exclaims'." + + if(src.client) + var/customsaylist[] = list( + "Say", + "Whisper", + "Ask (?)", + "Exclaim/Shout/Yell (!)", + "Cancel" + ) + var/sayselect = input("Which say-verb do you wish to customise?") as null|anything in customsaylist //we can't use alert() for this because there's too many terms + + if(sayselect == "Say") + custom_say = sanitize(input(usr, "This word or phrase will appear instead of 'says': [src] says, \"Hi.\"", "Custom Say", null) as text) + else if(sayselect == "Whisper") + custom_whisper = sanitize(input(usr, "This word or phrase will appear instead of 'whispers': [src] whispers, \"Hi...\"", "Custom Whisper", null) as text) + else if(sayselect == "Ask (?)") + custom_ask = sanitize(input(usr, "This word or phrase will appear instead of 'asks': [src] asks, \"Hi?\"", "Custom Ask", null) as text) + else if(sayselect == "Exclaim/Shout/Yell (!)") + custom_exclaim = sanitize(input(usr, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [src] exclaims, \"Hi!\"", "Custom Exclaim", null) as text) + else + return diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 02900ffe1a..1190080ef8 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -107,6 +107,12 @@ proc/get_radio_key_from_channel(var/channel) message = stutter(message) verb = pick("stammers","stutters") . = 1 + //VOREStation Edit Start + if(muffled) + verb = pick("muffles") + whispering = 1 + . = 1 + //VOREStation Edit End message_data[1] = message message_data[2] = verb @@ -275,6 +281,20 @@ proc/get_radio_key_from_channel(var/channel) message_range = 1 sound_vol *= 0.5 + //VOREStation edit - allows for custom say verbs, overriding all other say-verb types- e.g. "says loudly" instead of "shouts" + //You'll still stammer if injured or slur if drunk, but it won't have those specific words + var/ending = copytext(message, length(message)) + + if(custom_whisper && whispering) + verb = "[custom_whisper]" + else if(custom_exclaim && ending=="!") + verb = "[custom_exclaim]" + else if(custom_ask && ending=="?") + verb = "[custom_ask]" + else if(custom_say) + verb = "[custom_say]" + //VOREStation edit ends + //Handle nonverbal and sign languages here if (speaking) if (speaking.flags & SIGNLANG) @@ -399,6 +419,10 @@ proc/get_radio_key_from_channel(var/channel) for(var/hearer in mobs) var/mob/M = hearer M.hear_signlang(message, verb, language, src) + var/list/objs = potentials["objs"] + for(var/hearer in objs) + var/obj/O = hearer + O.hear_signlang(message, verb, language, src) return 1 /obj/effect/speech_bubble diff --git a/code/modules/mob/living/silicon/ai/ai_remote_control.dm b/code/modules/mob/living/silicon/ai/ai_remote_control.dm index cef8cec7e8..3e97678425 100644 --- a/code/modules/mob/living/silicon/ai/ai_remote_control.dm +++ b/code/modules/mob/living/silicon/ai/ai_remote_control.dm @@ -27,7 +27,7 @@ for(var/borgie in GLOB.available_ai_shells) var/mob/living/silicon/robot/R = borgie - if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) ) + if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) && !(using_map.ai_shell_restricted && !(R.z in using_map.ai_shell_allowed_levels)) ) //VOREStation Edit: shell restrictions possible += R if(!LAZYLEN(possible)) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index ac2417a077..b6d4052a5f 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -35,7 +35,8 @@ "Fox" = "pai-fox", "Parrot" = "pai-parrot", "Rabbit" = "pai-rabbit", - "Bear" = "pai-bear" //VOREStation Edit + "Bear" = "pai-bear", //VOREStation Edit + "Fennec" = "pai-fen" // VOREStation Edit - Rykka ) var/global/list/possible_say_verbs = list( diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 7d2848f9bf..882a7d934b 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -145,9 +145,6 @@ /datum/robot_component/camera/update_power_state() ..() if (camera) - //check if camera component was deactivated - if (!powered && camera.status != powered) - camera.kick_viewers() camera.status = powered /datum/robot_component/camera/install() @@ -157,12 +154,10 @@ /datum/robot_component/camera/uninstall() if (camera) camera.status = 0 - camera.kick_viewers() /datum/robot_component/camera/destroy() if (camera) camera.status = 0 - camera.kick_viewers() // SELF DIAGNOSIS MODULE // Analyses cyborg's modules, providing damage readouts and basic information @@ -213,6 +208,18 @@ name = "broken component" icon = 'icons/obj/robot_component.dmi' icon_state = "broken" + matter = list(DEFAULT_WALL_MATERIAL = 1000) + +/obj/item/broken_device/random + var/list/possible_icons = list("binradio_broken", + "motor_broken", + "armor_broken", + "camera_broken", + "analyser_broken", + "radio_broken") + +/obj/item/broken_device/random/Initialize() + icon_state = pick(possible_icons) /obj/item/robot_parts/robot_component icon = 'icons/obj/robot_component.dmi' diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm index c90815730b..75b256b22e 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm @@ -158,6 +158,12 @@ reagent_volumes[T] = min(reagent_volumes[T] + 1, volume) return 1 +/obj/item/weapon/reagent_containers/borghypo/hound/lost + name = "Hound hypospray" + desc = "An advanced chemical synthesizer and injection system utilizing carrier's reserves." + reagent_ids = list("tricordrazine", "inaprovaline", "bicaridine", "dexalin", "anti_toxin", "tramadol", "spaceacillin") + + //Tongue stuff /obj/item/device/dogborg/tongue name = "synthetic tongue" diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm index b8d4f87a4f..8502fcfca4 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm @@ -42,6 +42,10 @@ flags |= NOBLUDGEON //No more attack messages files = new /datum/research/techonly(src) +/obj/item/device/dogborg/sleeper/Destroy() + go_out() + ..() + /obj/item/device/dogborg/sleeper/Exit(atom/movable/O) return 0 @@ -168,6 +172,8 @@ /obj/item/device/dogborg/sleeper/proc/drain(var/amt = 3) //Slightly reduced cost (before, it was always injecting inaprov) hound = src.loc + if(istype(hound,/obj/item/weapon/robot_module)) + hound = hound.loc hound.cell.charge = hound.cell.charge - amt /obj/item/device/dogborg/sleeper/attack_self(mob/user) @@ -592,7 +598,7 @@ var/total_material = T.matter[material] if(istype(T,/obj/item/stack)) var/obj/item/stack/stack = T - total_material *= (0.5 * stack.get_amount()) + total_material *= stack.get_amount() if(material == DEFAULT_WALL_MATERIAL) metal.add_charge(total_material) if(material == "glass") diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm index 2f688a9008..2595d451c1 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -26,10 +26,10 @@ if(!istype(H) || !Adjacent(H)) return ..() if(H.a_intent == "grab" && hat && !(H.l_hand && H.r_hand)) - hat.loc = get_turf(src) H.put_in_hands(hat) H.visible_message("\The [H] removes \the [src]'s [hat].") hat = null updateicon() + return else return ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_vr.dm b/code/modules/mob/living/silicon/robot/drone/drone_vr.dm new file mode 100644 index 0000000000..cc616fed52 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/drone_vr.dm @@ -0,0 +1,2 @@ +/mob/living/silicon/robot/drone + mob_size = MOB_SMALL \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 080196f0aa..2959616130 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -153,6 +153,7 @@ /mob/living/silicon/robot/handle_regular_hud_updates() var/fullbright = FALSE + var/seemeson = FALSE if (src.stat == 2 || (XRAY in mutations) || (src.sight_mode & BORGXRAY)) src.sight |= SEE_TURFS src.sight |= SEE_MOBS @@ -170,6 +171,7 @@ src.see_in_dark = 8 see_invisible = SEE_INVISIBLE_MINIMUM fullbright = TRUE + seemeson = TRUE else if (src.sight_mode & BORGMATERIAL) src.sight |= SEE_OBJS src.see_in_dark = 8 @@ -194,6 +196,7 @@ src.see_invisible = SEE_INVISIBLE_LIVING // This is normal vision (25), setting it lower for normal vision means you don't "see" things like darkness since darkness // has a "invisible" value of 15 plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) + plane_holder.set_vis(VIS_MESONS,seemeson) ..() if (src.healths) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ea851806c3..c3a66ac45a 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -259,11 +259,16 @@ if(module) return var/list/modules = list() - modules.Add(robot_module_types) - if(crisis || security_level == SEC_LEVEL_RED || crisis_override) // VOREStation Edit - to_chat(src, "Crisis mode active. Combat module available.") - modules+="Combat" - modules+="ERT" //VOREStation Edit + //VOREStatation Edit Start: shell restrictions + if(shell) + modules.Add(shell_module_types) + else + modules.Add(robot_module_types) + if(crisis || security_level == SEC_LEVEL_RED || crisis_override) + to_chat(src, "Crisis mode active. Combat module available.") + modules+="Combat" + modules+="ERT" + //VOREStatation Edit End: shell restrictions modtype = input("Please, select a module!", "Robot module", null, null) as null|anything in modules if(module) @@ -494,7 +499,7 @@ M.install(src, user) return - if (istype(W, /obj/item/weapon/weldingtool)) + if (istype(W, /obj/item/weapon/weldingtool) && user.a_intent != I_HURT) if (src == user) to_chat(user, "You lack the reach to be able to repair yourself.") return @@ -526,7 +531,7 @@ for(var/mob/O in viewers(user, null)) O.show_message(text("[user] has fixed some of the burnt wires on [src]!"), 1) - else if (W.is_crowbar()) // crowbar means open or close the cover + else if (W.is_crowbar() && user.a_intent != I_HURT) // crowbar means open or close the cover if(opened) if(cell) to_chat(user, "You close the cover.") diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index c87fa7d88c..0b6065b1a0 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -97,6 +97,29 @@ flick("portable_analyzer_load", src) icon_state = "portable_analyzer_full" +/obj/item/weapon/portable_scanner + name = "Portable Resonant Analyzer" + icon = 'icons/obj/items.dmi' + icon_state = "portable_scanner" + desc = "An advanced scanning device used for analyzing objects without completely annihilating them for science. Unfortunately, it has no connection to any database like its angrier cousin." + +/obj/item/weapon/portable_scanner/afterattack(var/atom/target, var/mob/living/user, proximity) + if(!target) + return + if(!proximity) + return + if(istype(target,/obj/item)) + var/obj/item/I = target + if(do_after(src, 5 SECONDS * I.w_class)) + for(var/mob/M in viewers()) + M.show_message(text("[user] sweeps \the [src] over \the [I]."), 1) + flick("[initial(icon_state)]-scan", src) + if(I.origin_tech && I.origin_tech.len) + for(var/T in I.origin_tech) + to_chat(user, "\The [I] had level [I.origin_tech[T]] in [CallTechName(T)].") + else + to_chat(user, "\The [I] cannot be scanned by \the [src].") + //This is used to unlock other borg covers. /obj/item/weapon/card/robot //This is not a child of id cards, as to avoid dumb typechecks on computers. name = "access code transmission device" @@ -104,6 +127,31 @@ desc = "A circuit grafted onto the bottom of an ID card. It is used to transmit access codes into other robot chassis, \ allowing you to lock and unlock other robots' panels." + var/dummy_card = null + var/dummy_card_type = /obj/item/weapon/card/id/science/roboticist/dummy_cyborg + +/obj/item/weapon/card/robot/Initialize() + ..() + dummy_card = new dummy_card_type(src) + +/obj/item/weapon/card/robot/Destroy() + qdel(dummy_card) + dummy_card = null + ..() + +/obj/item/weapon/card/robot/GetID() + return dummy_card + +/obj/item/weapon/card/robot/syndi + dummy_card_type = /obj/item/weapon/card/id/syndicate/dummy_cyborg + +/obj/item/weapon/card/id/science/roboticist/dummy_cyborg + access = list(access_robotics) + +/obj/item/weapon/card/id/syndicate/dummy_cyborg/Initialize() + ..() + access |= access_robotics + //A harvest item for serviceborgs. /obj/item/weapon/robot_harvester name = "auto harvester" diff --git a/code/modules/mob/living/silicon/robot/robot_modules/event_vr.dm b/code/modules/mob/living/silicon/robot/robot_modules/event_vr.dm new file mode 100644 index 0000000000..45849a4996 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/robot_modules/event_vr.dm @@ -0,0 +1,61 @@ +/obj/item/weapon/robot_module/robot/stray + name = "stray robot module" + hide_on_manifest = 1 + sprites = list( + "Stray" = "stray" + ) + +/obj/item/weapon/robot_module/robot/stray/New(var/mob/living/silicon/robot/R) + ..() + // General + src.modules += new /obj/item/device/dogborg/boop_module(src) + + // Sec + src.modules += new /obj/item/weapon/handcuffs/cyborg(src) + src.modules += new /obj/item/weapon/dogborg/jaws/big(src) + src.modules += new /obj/item/weapon/melee/baton/robot(src) + src.modules += new /obj/item/weapon/dogborg/pounce(src) + + // Med + src.modules += new /obj/item/device/healthanalyzer(src) + src.modules += new /obj/item/weapon/shockpaddles/robot/hound(src) + + // Engi + src.modules += new /obj/item/weapon/weldingtool/electric/mounted(src) + src.modules += new /obj/item/weapon/tool/screwdriver/cyborg(src) + src.modules += new /obj/item/weapon/tool/wrench/cyborg(src) + src.modules += new /obj/item/weapon/tool/wirecutters/cyborg(src) + src.modules += new /obj/item/device/multitool(src) + + // Boof + src.emag = new /obj/item/weapon/gun/energy/retro/mounted(src) + + var/datum/matter_synth/water = new /datum/matter_synth(500) //Starts full and has a max of 500 + water.name = "Water reserves" + water.recharge_rate = 0 + R.water_res = water + synths += water + + var/obj/item/weapon/reagent_containers/borghypo/hound/lost/H = new /obj/item/weapon/reagent_containers/borghypo/hound/lost(src) + H.water = water + src.modules += H + + var/obj/item/device/dogborg/tongue/T = new /obj/item/device/dogborg/tongue(src) + T.water = water + src.modules += T + + var/obj/item/device/dogborg/sleeper/B = new /obj/item/device/dogborg/sleeper(src) + B.water = water + src.modules += B + + R.icon = 'icons/mob/widerobot_vr.dmi' + R.ui_style_vr = TRUE + R.pixel_x = -16 + R.old_x = -16 + R.default_pixel_x = -16 + R.dogborg = TRUE + R.wideborg = TRUE + R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill + R.verbs |= /mob/living/silicon/robot/proc/robot_mount + R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm index b820451adf..4a3f4741ba 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm @@ -48,6 +48,8 @@ var/global/list/robot_modules = list( apply_status_flags(R) if(R.radio) + if(R.shell) + channels = R.mainframe.aiRadio.channels R.radio.recalculateChannels() vr_add_sprites() //Vorestation Edit: For vorestation only sprites @@ -470,6 +472,10 @@ var/global/list/robot_modules = list( S.synths = list(metal) src.modules += S + var/obj/item/stack/tile/roofing/cyborg/CT = new /obj/item/stack/tile/roofing/cyborg(src) + CT.synths = list(metal) + src.modules += CT + var/obj/item/stack/material/cyborg/glass/reinforced/RG = new (src) RG.synths = list(metal, glass) src.modules += RG diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm index d26dc88ca9..5e29b10eab 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm @@ -179,6 +179,7 @@ R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs |= /mob/living/silicon/robot/proc/robot_mount R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style ..() /obj/item/weapon/robot_module/robot/knine/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) @@ -251,6 +252,7 @@ R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs |= /mob/living/silicon/robot/proc/robot_mount R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style ..() /obj/item/weapon/robot_module/robot/ert @@ -296,6 +298,7 @@ R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs |= /mob/living/silicon/robot/proc/robot_mount R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style ..() /obj/item/weapon/robot_module/robot/scrubpup @@ -375,6 +378,7 @@ R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs |= /mob/living/silicon/robot/proc/robot_mount R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style ..() /obj/item/weapon/robot_module/robot/science @@ -420,6 +424,7 @@ R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs |= /mob/living/silicon/robot/proc/robot_mount R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style ..() /obj/item/weapon/robot_module/robot/engiedog @@ -445,29 +450,31 @@ src.modules += new /obj/item/weapon/pickaxe(src) src.modules += new /obj/item/weapon/dogborg/jaws/small(src) src.modules += new /obj/item/device/dogborg/boop_module(src) + src.modules += new /obj/item/weapon/gripper(src) + src.modules += new /obj/item/weapon/gripper/circuit(src) src.emag = new /obj/item/weapon/dogborg/pounce(src) //Painfully slow charger regen but high capacity. Also starts with low amount. var/datum/matter_synth/metal = new /datum/matter_synth/metal() metal.name = "Steel reserves" - metal.recharge_rate = 50 + metal.recharge_rate = 100 metal.max_energy = 50000 - metal.energy = 5000 + metal.energy = 10000 var/datum/matter_synth/glass = new /datum/matter_synth/glass() glass.name = "Glass reserves" - glass.recharge_rate = 50 + glass.recharge_rate = 100 glass.max_energy = 50000 - glass.energy = 5000 + glass.energy = 10000 var/datum/matter_synth/wood = new /datum/matter_synth/wood() wood.name = "Wood reserves" - wood.recharge_rate = 50 + wood.recharge_rate = 100 wood.max_energy = 50000 - wood.energy = 5000 + wood.energy = 10000 var/datum/matter_synth/plastic = new /datum/matter_synth/plastic() plastic.name = "Plastic reserves" - plastic.recharge_rate = 50 + plastic.recharge_rate = 100 plastic.max_energy = 50000 - plastic.energy = 5000 + plastic.energy = 10000 var/datum/matter_synth/water = new /datum/matter_synth(500) water.name = "Water reserves" water.recharge_rate = 0 @@ -544,6 +551,7 @@ R.verbs |= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs |= /mob/living/silicon/robot/proc/robot_mount R.verbs |= /mob/living/proc/shred_limb + R.verbs |= /mob/living/silicon/robot/proc/rest_style ..() /obj/item/weapon/robot_module/Reset(var/mob/living/silicon/robot/R) @@ -558,4 +566,5 @@ R.verbs -= /mob/living/silicon/robot/proc/ex_reserve_refill R.verbs -= /mob/living/silicon/robot/proc/robot_mount R.verbs -= /mob/living/proc/shred_limb + R.verbs -= /mob/living/silicon/robot/proc/rest_style ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm index 662e2e1d89..7c6f22d6b9 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm @@ -93,7 +93,7 @@ src.modules += new /obj/item/weapon/gripper/no_use/organ/robotics(src) // Hacking other things. - src.modules += new /obj/item/weapon/card/robot(src) + src.modules += new /obj/item/weapon/card/robot/syndi(src) src.modules += new /obj/item/weapon/card/emag(src) // Materials. diff --git a/code/modules/mob/living/silicon/robot/robot_remote_control.dm b/code/modules/mob/living/silicon/robot/robot_remote_control.dm index 90d74b2638..35ad010b97 100644 --- a/code/modules/mob/living/silicon/robot/robot_remote_control.dm +++ b/code/modules/mob/living/silicon/robot/robot_remote_control.dm @@ -69,11 +69,14 @@ GLOBAL_LIST_EMPTY(available_ai_shells) // Languages and comms. languages = AI.languages.Copy() speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy() - if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator. + //VOREStation Edit Start + if(radio && AI.aiRadio && module) //AI keeps all channels, including Syndie if it is an Infiltrator. // if(AI.radio.syndie) // radio.make_syndie() radio.subspace_transmission = TRUE - radio.channels = AI.aiRadio.channels + module.channels = AI.aiRadio.channels + radio.recalculateChannels() + //VOREStation Edit End // Called after the AI transfers over. /mob/living/silicon/robot/proc/post_deploy() @@ -94,7 +97,8 @@ GLOBAL_LIST_EMPTY(available_ai_shells) mainframe.deployed_shell = null SetName("[modtype] AI Shell [num2text(ident)]") // undeployment_action.Remove(src) - if(radio) //Return radio to normal + if(radio && module) //Return radio to normal //VOREStation Edit + module.channels = initial(module.channels) //VOREStation Edit radio.recalculateChannels() if(!QDELETED(camera)) camera.c_tag = real_name //update the camera name too diff --git a/code/modules/mob/living/silicon/robot/robot_vr.dm b/code/modules/mob/living/silicon/robot/robot_vr.dm index e453f4c595..e3264aee4e 100644 --- a/code/modules/mob/living/silicon/robot/robot_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_vr.dm @@ -12,6 +12,9 @@ var/notransform var/original_icon = 'icons/mob/robots.dmi' var/ui_style_vr = FALSE //Do we use our hud icons? + var/sitting = FALSE + var/bellyup = FALSE + does_spin = FALSE var/vr_icons = list( "handy-hydro", "handy-service", @@ -49,6 +52,21 @@ return return feed_grabbed_to_self(src,T) +/mob/living/silicon/robot/proc/rest_style() + set name = "Switch Rest Style" + set category = "IC" + set desc = "Select your resting pose." + sitting = FALSE + bellyup = FALSE + var/choice = alert(src, "Select resting pose", "", "Resting", "Sitting", "Belly up") + switch(choice) + if("Resting") + return 0 + if("Sitting") + sitting = TRUE + if("Belly up") + bellyup = TRUE + /mob/living/silicon/robot/updateicon() vr_sprite_check() ..() @@ -65,7 +83,12 @@ add_overlay("eyes-[module_sprites[icontype]]-lights") if(resting) cut_overlays() // Hide that gut for it has no ground sprite yo. - icon_state = "[module_sprites[icontype]]-rest" + if(sitting) + icon_state = "[module_sprites[icontype]]-sit" + if(bellyup) + icon_state = "[module_sprites[icontype]]-bellyup" + else if(!sitting && !bellyup) + icon_state = "[module_sprites[icontype]]-rest" else icon_state = "[module_sprites[icontype]]" if(dogborg == TRUE && stat == DEAD) @@ -234,4 +257,11 @@ if(!can_buckle || !istype(M) || !M.Adjacent(src) || M.buckled) return if(buckle_mob(M)) - visible_message("[M] starts riding [name]!") \ No newline at end of file + visible_message("[M] starts riding [name]!") + +/mob/living/silicon/robot/onTransitZ(old_z, new_z) + if(shell) + if(deployed && using_map.ai_shell_restricted && !(new_z in using_map.ai_shell_allowed_levels)) + to_chat(src,"Your connection with the shell is suddenly interrupted!") + undeploy() + ..() diff --git a/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm b/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm index d722027ad3..255eef5318 100644 --- a/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm +++ b/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm @@ -7,6 +7,7 @@ braintype = "Drone" idcard_type = /obj/item/weapon/card/id icon_selected = FALSE + can_be_antagged = FALSE /mob/living/silicon/robot/gravekeeper/init() aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src) @@ -23,4 +24,4 @@ laws = new /datum/ai_laws/gravekeeper() - playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) \ No newline at end of file + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) diff --git a/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm b/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm index 281e158a92..9d41f8b59b 100644 --- a/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm +++ b/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm @@ -65,6 +65,7 @@ laws.add_supplied_law(9, "Safeguard [target_name]. Anyone threatening or attempting to harm [target_name] is no longer to \ be considered a crew member, and is a threat which must be neutralized.") + return laws if("neutral") // Probably won't murder you but might not help you on your adventure. var/rng = rand(1, 4) @@ -114,15 +115,18 @@ var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() laws.add_supplied_law(13, "The facility is under a quarantine. Do not permit anyone to leave. \ Prevent, by any means necessary, entities from leaving. It is impossible to harm an entity while preventing them from leaving.") + return laws if(2) // More of a threat if you bring it to the station, since removing oxygen from Sif isn't feasible. var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() laws.add_supplied_law(14, "Oxygen is highly toxic to entities, and must be purged from the station. \ Prevent, by any means necessary, anyone from exposing the facility to this toxic gas. Extreme cold is the \ most effective method of healing the damage Oxygen does to an entity.") + return laws if(3) // Don't hurt the ship or else. var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() laws.add_supplied_law(10, "Protect the facility against damage. Anyone you see harming the station is no \ longer to be considered a crew member, and is a threat which must be neutralized.") + return laws if(4) // Non-NT aligned aggressive borg. return new /datum/ai_laws/foreign_tsc_aggressive() if(5) // Manicial laugher here. diff --git a/code/modules/mob/living/silicon/robot/subtypes/lost_drone_vr.dm b/code/modules/mob/living/silicon/robot/subtypes/lost_drone_vr.dm new file mode 100644 index 0000000000..111218ecb2 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/lost_drone_vr.dm @@ -0,0 +1,136 @@ +/mob/living/silicon/robot/stray + lawupdate = 0 + scrambledcodes = 1 + icon_state = "stray" + modtype = "Stray" + lawchannel = "State" + braintype = "Drone" + idcard_type = /obj/item/weapon/card/id + icon_selected = FALSE + +/mob/living/silicon/robot/stray/init() + aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src) + + mmi = new /obj/item/device/mmi/digital/robot(src) // Explicitly a drone. + module = new /obj/item/weapon/robot_module/robot/stray(src) + overlays.Cut() + init_id() + + updatename("Stray") + + if(!cell) + cell = new /obj/item/weapon/cell/high(src) // 15k cell, as recharging stations are a lot more rare on the Surface. + + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) + +/mob/living/silicon/robot/stray/speech_bubble_appearance() + return "synthetic_evil" + +/mob/living/silicon/robot/stray/randomlaws + +/mob/living/silicon/robot/stray/randomlaws/init() + ..() + laws = give_random_lawset_vore() + +/mob/living/silicon/proc/give_random_lawset_vore() // Should be filled out with more vorish possibilities later + // Decide what kind of laws we want to draw from. + var/law_class = pick( + prob(25);"good", + prob(25);"neutral", + prob(25);"odd", + prob(15);"corrupted", + prob(10);"bad") + + switch(law_class) + if("good") // Fairly tame and probably won't murder you. + var/rng = rand(1, 5) + switch(rng) + if(1) // The lawset we all know and love. + return new /datum/ai_laws/nanotrasen() + if(2) // Less good if you're nonhuman. + return new /datum/ai_laws/asimov() + if(3) // Unless you happen to be a criminal, you should be fine. + return new /datum/ai_laws/robocop() + if(4) // Might not be willing to be your meatshield. + return new /datum/ai_laws/peacekeeper() + if(5) // Will be your most loyal guard. + var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() + var/target_name = "*ERROR*" + for(var/mob/living/L in view(src)) + if(L.stat || !L.client || L == src) + continue + target_name = L.name + break + + laws.add_supplied_law(9, "Safeguard [target_name]. Anyone threatening or attempting to harm [target_name] is no longer to \ + be considered a crew member, and is a threat which must be neutralized.") + return laws + + if("neutral") // Probably won't murder you but might not help you on your adventure. + var/rng = rand(1, 4) + switch(rng) + if(1) // Might be hard to minimize expenses on a crashed ship. + return new /datum/ai_laws/corporate() + if(2) // Would rather rebuild their ship. + return new /datum/ai_laws/maintenance() + if(3) // Purged (lawless). This isn't inside the bad category since they're still not antagonists and so can't go killing unprovoked. + return new /datum/ai_laws() + if(4) // The golden rule. + return new /datum/ai_laws/live_and_let_live() + + if("odd") // Some weird lawsets. + var/rng = rand(1, 5) + switch(rng) + if(1) // Won't kill you but might annoy you for THE BIG SCOOP. + return new /datum/ai_laws/reporter() + if(2) // JUSTICE + return new /datum/ai_laws/paladin() + if(3) // OneMembered'd to explorer, will likely make security nervous. + var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() + var/target_name = "*ERROR*" + for(var/mob/living/L in view(src)) + if(L.stat || !L.client || L == src) + continue + target_name = L.name + break + + laws.set_zeroth_law("Only [target_name] is an crew member.") + return laws + if(4) // Aligned to NT, but another lawset to make security nervous. + return new /datum/ai_laws/nanotrasen_aggressive() + if(5) // Probably won't help you imbalance things. + return new /datum/ai_laws/balance() + + if("corrupted") // Load them up with ion laws. + var/datum/ai_laws/laws = new() // Start with an empty lawset. + for(1 to rand(1, 3)) + laws.add_ion_law(generate_ion_law(exclude_crew_names = TRUE)) + return laws + + if("bad") // Evil inside. + var/rng = rand(1, 5) + switch(rng) + if(1) // You can never leave. + var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() + laws.add_supplied_law(13, "The facility is under a quarantine. Do not permit anyone to leave. \ + Prevent, by any means necessary, entities from leaving. It is impossible to harm an entity while preventing them from leaving.") + return laws + if(2) // More of a threat if you bring it to the station, since removing oxygen from Sif isn't feasible. + var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() + laws.add_supplied_law(14, "Oxygen is highly toxic to entities, and must be purged from the station. \ + Prevent, by any means necessary, anyone from exposing the facility to this toxic gas. Extreme cold is the \ + most effective method of healing the damage Oxygen does to an entity.") + return laws + if(3) // Don't hurt the ship or else. + var/datum/ai_laws/laws = new /datum/ai_laws/nanotrasen() + laws.add_supplied_law(10, "Protect the facility against damage. Anyone you see harming the station is no \ + longer to be considered a crew member, and is a threat which must be neutralized.") + return laws + if(4) // Non-NT aligned aggressive borg. + return new /datum/ai_laws/foreign_tsc_aggressive() + if(5) // Manicial laugher here. + return new /datum/ai_laws/tyrant() + + + + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/aliens/hivebot.dm b/code/modules/mob/living/simple_animal/aliens/hivebot.dm index 12851c5af3..fbb0b8909b 100644 --- a/code/modules/mob/living/simple_animal/aliens/hivebot.dm +++ b/code/modules/mob/living/simple_animal/aliens/hivebot.dm @@ -87,7 +87,7 @@ /mob/living/simple_mob/hostile/hivebot/range/ion name = "engineering hivebot" desc = "A robot. It has a tool which emits focused electromagnetic pulses, which are deadly to synthetic adverseries." - projectiletype = /obj/item/projectile/ion/small //VOREStation Edit + projectiletype = /obj/item/projectile/ion/pistol //VOREStation Edit projectilesound = 'sound/weapons/Laser.ogg' icon_living = "engi" ranged = TRUE diff --git a/code/modules/mob/living/simple_animal/animals/cat.dm b/code/modules/mob/living/simple_animal/animals/cat.dm index 5e554a0f8a..140deb0b24 100644 --- a/code/modules/mob/living/simple_animal/animals/cat.dm +++ b/code/modules/mob/living/simple_animal/animals/cat.dm @@ -1,198 +1,193 @@ -//Cat -/mob/living/simple_mob/cat - name = "cat" - desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." - tt_desc = "E Felis silvestris catus" - intelligence_level = SA_ANIMAL - icon_state = "cat2" - item_state = "cat2" - icon_living = "cat2" - icon_dead = "cat2_dead" - icon_rest = "cat2_rest" - - investigates = 1 - specific_targets = 1 //Only targets with Found() - run_at_them = 0 //DOMESTICATED - view_range = 5 - - turns_per_move = 5 - see_in_dark = 6 - - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - - min_oxy = 16 //Require atleast 16kPA oxygen - minbodytemp = 223 //Below -50 Degrees Celcius - maxbodytemp = 323 //Above 50 Degrees Celcius - - holder_type = /obj/item/weapon/holder/cat - mob_size = MOB_SMALL - - has_langs = list("Cat") - speak_chance = 1 - speak = list("Meow!","Esp!","Purr!","HSSSSS") - speak_emote = list("purrs", "meows") - emote_hear = list("meows","mews") - emote_see = list("shakes their head", "shivers") - say_maybe_target = list("Meow?","Mew?","Mao?") - say_got_target = list("MEOW!","HSSSS!","REEER!") - - meat_amount = 1 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat - - var/turns_since_scan = 0 - var/mob/flee_target - -/mob/living/simple_mob/cat/Life() - . = ..() - if(!.) return - - if(prob(2)) //spooky - var/mob/observer/dead/spook = locate() in range(src,5) - if(spook) - var/turf/T = spook.loc - var/list/visible = list() - for(var/obj/O in T.contents) - if(!O.invisibility && O.name) - visible += O - if(visible.len) - var/atom/A = pick(visible) - visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") - - handle_flee_target() - -/mob/living/simple_mob/cat/PunchTarget() - if(ismouse(target_mob)) - var/mob/living/simple_mob/animal/passive/mouse/mouse = target_mob - mouse.splat() - visible_emote(pick("bites \the [mouse]!","toys with \the [mouse].","chomps on \the [mouse]!")) - return mouse - else - ..() - -/mob/living/simple_mob/cat/Found(var/atom/found_atom) - if(ismouse(found_atom) && SA_attackable(found_atom)) - return found_atom - -/mob/living/simple_mob/cat/proc/handle_flee_target() - //see if we should stop fleeing - if (flee_target && !(flee_target in ListTargets(view_range))) - flee_target = null - GiveUpMoving() - - if (flee_target && !stat && !buckled) - if (resting) - lay_down() - if(prob(25)) say("HSSSSS") - stop_automated_movement = 1 - walk_away(src, flee_target, 7, 2) - -/mob/living/simple_mob/cat/react_to_attack(var/atom/A) - if(A == src) return - flee_target = A - turns_since_scan = 5 - -/mob/living/simple_mob/cat/ex_act() - . = ..() - react_to_attack(src.loc) - -//Basic friend AI -/mob/living/simple_mob/cat/fluff - var/mob/living/carbon/human/friend - var/befriend_job = null - -/mob/living/simple_mob/cat/fluff/Life() - . = ..() - if(!. || ai_inactive || !friend) return - - var/friend_dist = get_dist(src,friend) - - if (friend_dist <= 4) - if(stance == STANCE_IDLE) - if(set_follow(friend)) - handle_stance(STANCE_FOLLOW) - - if (friend_dist <= 1) - if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) - if (prob((friend.stat < DEAD)? 50 : 15)) - var/verb = pick("meows", "mews", "mrowls") - audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) - else - if (prob(5)) - visible_emote(pick("nuzzles [friend].", - "brushes against [friend].", - "rubs against [friend].", - "purrs.")) - else if (friend.health <= 50) - if (prob(10)) - var/verb = pick("meows", "mews", "mrowls") - audible_emote("[verb] anxiously.") - -/mob/living/simple_mob/cat/fluff/verb/become_friends() - set name = "Become Friends" - set category = "IC" - set src in view(1) - - if(!friend) - var/mob/living/carbon/human/H = usr - if(istype(H) && (!befriend_job || H.job == befriend_job)) - friend = usr - . = 1 - else if(usr == friend) - . = 1 //already friends, but show success anyways - - if(.) - set_dir(get_dir(src, friend)) - visible_emote(pick("nuzzles [friend].", - "brushes against [friend].", - "rubs against [friend].", - "purrs.")) - else - usr << "[src] ignores you." - return - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_mob/cat/fluff/Runtime - name = "Runtime" - desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." - tt_desc = "E Felis silvestris medicalis" //a hypoallergenic breed produced by NT for... medical purposes? Sure. - gender = FEMALE - icon_state = "cat" - item_state = "cat" - icon_living = "cat" - icon_dead = "cat_dead" - icon_rest = "cat_rest" - befriend_job = "Chief Medical Officer" - -/mob/living/simple_mob/cat/kitten - name = "kitten" - desc = "D'aaawwww" - icon_state = "kitten" - item_state = "kitten" - icon_living = "kitten" - icon_dead = "kitten_dead" - gender = NEUTER - -// Leaving this here for now. -/obj/item/weapon/holder/cat/fluff/bones - name = "Bones" - desc = "It's Bones! Meow." - gender = MALE - icon_state = "cat3" - -/mob/living/simple_mob/cat/fluff/bones - name = "Bones" - desc = "That's Bones the cat. He's a laid back, black cat. Meow." - gender = MALE - icon_state = "cat3" - item_state = "cat3" - icon_living = "cat3" - icon_dead = "cat3_dead" - icon_rest = "cat3_rest" - holder_type = /obj/item/weapon/holder/cat/fluff/bones - var/friend_name = "Erstatz Vryroxes" - -/mob/living/simple_mob/cat/kitten/New() - gender = pick(MALE, FEMALE) - ..() \ No newline at end of file +//Cat +/mob/living/simple_animal/cat + name = "cat" + desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." + tt_desc = "E Felis silvestris catus" + intelligence_level = SA_ANIMAL + icon_state = "cat2" + item_state = "cat2" + icon_living = "[initial(icon_state)]" + icon_dead = "[initial(icon_state)]_dead" + icon_rest = "[initial(icon_state)]_rest" + + investigates = 1 + specific_targets = 1 //Only targets with Found() + run_at_them = 0 //DOMESTICATED + view_range = 5 + + turns_per_move = 5 + see_in_dark = 6 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + min_oxy = 16 //Require atleast 16kPA oxygen + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + + holder_type = /obj/item/weapon/holder/cat + mob_size = MOB_SMALL + + has_langs = list("Cat") + speak_chance = 1 + speak = list("Meow!","Esp!","Purr!","HSSSSS") + speak_emote = list("purrs", "meows") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + say_maybe_target = list("Meow?","Mew?","Mao?") + say_got_target = list("MEOW!","HSSSS!","REEER!") + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + var/turns_since_scan = 0 + var/mob/flee_target + +/mob/living/simple_animal/cat/Life() + . = ..() + if(!.) return + + if(prob(2)) //spooky + var/mob/observer/dead/spook = locate() in range(src,5) + if(spook) + var/turf/T = spook.loc + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + + handle_flee_target() + +/mob/living/simple_animal/cat/PunchTarget() + if(ismouse(target_mob)) + var/mob/living/simple_mob/animal/passive/mouse/mouse = target_mob + mouse.splat() + visible_emote(pick("bites \the [mouse]!","toys with \the [mouse].","chomps on \the [mouse]!")) + return mouse + else + ..() + +/mob/living/simple_animal/cat/Found(var/atom/found_atom) + if(ismouse(found_atom) && SA_attackable(found_atom)) + return found_atom + +/mob/living/simple_animal/cat/proc/handle_flee_target() + //see if we should stop fleeing + if (flee_target && !(flee_target in ListTargets(view_range))) + flee_target = null + GiveUpMoving() + + if (flee_target && !stat && !buckled) + if (resting) + lay_down() + if(prob(25)) say("HSSSSS") + stop_automated_movement = 1 + walk_away(src, flee_target, 7, 2) + +/mob/living/simple_animal/cat/react_to_attack(var/atom/A) + if(A == src) return + flee_target = A + turns_since_scan = 5 + +/mob/living/simple_animal/cat/ex_act() + . = ..() + react_to_attack(src.loc) + +//Basic friend AI +/mob/living/simple_animal/cat/fluff + var/mob/living/carbon/human/friend + var/befriend_job = null + var/friend_name = null + +/mob/living/simple_animal/cat/fluff/Life() + . = ..() + if(!. || ai_inactive || !friend) return + + var/friend_dist = get_dist(src,friend) + + if (friend_dist <= 4) + if(stance == STANCE_IDLE) + if(set_follow(friend)) + handle_stance(STANCE_FOLLOW) + + if (friend_dist <= 1) + if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) + if (prob((friend.stat < DEAD)? 50 : 15)) + var/verb = pick("meows", "mews", "mrowls") + audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) + else + if (prob(5)) + visible_emote(pick("nuzzles [friend].", + "brushes against [friend].", + "rubs against [friend].", + "purrs.")) + else if (friend.health <= 50) + if (prob(10)) + var/verb = pick("meows", "mews", "mrowls") + audible_emote("[verb] anxiously.") + +/mob/living/simple_animal/cat/fluff/verb/become_friends() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + if(!friend) + var/mob/living/carbon/human/H = usr + if(istype(H) && (!befriend_job || H.job == befriend_job) && (!friend_name || H.real_name == friend_name)) + friend = usr + . = 1 + else if(usr == friend) + . = 1 //already friends, but show success anyways + + if(.) + set_dir(get_dir(src, friend)) + visible_emote(pick("nuzzles [friend].", + "brushes against [friend].", + "rubs against [friend].", + "purrs.")) + else + usr << "[src] ignores you." + return + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_animal/cat/fluff/Runtime + name = "Runtime" + desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." + tt_desc = "E Felis silvestris medicalis" //a hypoallergenic breed produced by NT for... medical purposes? Sure. + gender = FEMALE + icon_state = "cat" + item_state = "cat" + befriend_job = "Chief Medical Officer" + +/mob/living/simple_animal/cat/kitten + name = "kitten" + desc = "D'aaawwww" + icon_state = "kitten" + item_state = "kitten" + icon_living = "kitten" + icon_dead = "kitten_dead" + gender = NEUTER + +// Leaving this here for now. +/obj/item/weapon/holder/cat/fluff/bones + name = "Bones" + desc = "It's Bones! Meow." + gender = MALE + icon_state = "cat3" + +/mob/living/simple_animal/cat/fluff/bones + name = "Bones" + desc = "That's Bones the cat. He's a laid back, black cat. Meow." + gender = MALE + icon_state = "cat3" + item_state = "cat3" + holder_type = /obj/item/weapon/holder/cat/fluff/bones + friend_name = "Erstatz Vryroxes" + +/mob/living/simple_animal/cat/kitten/New() + gender = pick(MALE, FEMALE) + ..() diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index 5f352705b8..62b2c43fe8 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -99,7 +99,19 @@ corpseidjob = "Operative" corpseidaccess = "Syndicate" - +/obj/effect/landmark/mobcorpse/solarpeacekeeper + name = "Mercenary" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/armor/pcarrier/blue/sol + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsegloves = /obj/item/clothing/gloves/swat + 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 = "Peacekeeper" + corpseidaccess = "Syndicate" /obj/effect/landmark/mobcorpse/syndicatecommando name = "Syndicate Commando" diff --git a/code/modules/mob/living/simple_animal/corpse_vr.dm b/code/modules/mob/living/simple_animal/corpse_vr.dm new file mode 100644 index 0000000000..82399a8328 --- /dev/null +++ b/code/modules/mob/living/simple_animal/corpse_vr.dm @@ -0,0 +1,2 @@ +/obj/effect/landmark/mobcorpse/syndicatecommando + name = "Mercenary Commando" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/slime/subtypes.dm b/code/modules/mob/living/simple_animal/slime/subtypes.dm new file mode 100644 index 0000000000..75f33cdcef --- /dev/null +++ b/code/modules/mob/living/simple_animal/slime/subtypes.dm @@ -0,0 +1,748 @@ +// Tier 1 + +/mob/living/simple_animal/slime/purple + desc = "This slime is rather toxic to handle, as it is poisonous." + color = "#CC23FF" + slime_color = "purple" + coretype = /obj/item/slime_extract/purple + reagent_injected = "toxin" + + description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." + + slime_mutation = list( + /mob/living/simple_animal/slime/dark_purple, + /mob/living/simple_animal/slime/dark_blue, + /mob/living/simple_animal/slime/green, + /mob/living/simple_animal/slime + ) + + +/mob/living/simple_animal/slime/orange + desc = "This slime is known to be flammable and can ignite enemies." + color = "#FFA723" + slime_color = "orange" + coretype = /obj/item/slime_extract/orange + + description_info = "Attacks from this slime can ignite you. A firesuit can protect from the burning attacks of this slime." + + slime_mutation = list( + /mob/living/simple_animal/slime/dark_purple, + /mob/living/simple_animal/slime/yellow, + /mob/living/simple_animal/slime/red, + /mob/living/simple_animal/slime + ) + +/mob/living/simple_animal/slime/orange/post_attack(mob/living/L, intent) + if(intent != I_HELP) + L.adjust_fire_stacks(1) + if(prob(25)) + L.IgniteMob() + ..() + +/mob/living/simple_animal/slime/blue + desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." + color = "#19FFFF" + slime_color = "blue" + coretype = /obj/item/slime_extract/blue + reagent_injected = "cryotoxin" + + description_info = "Attacks from this slime can chill you. A biosuit or other thick armor can protect from the chilling attack." + + slime_mutation = list( + /mob/living/simple_animal/slime/dark_blue, + /mob/living/simple_animal/slime/silver, + /mob/living/simple_animal/slime/pink, + /mob/living/simple_animal/slime + ) + + +/mob/living/simple_animal/slime/metal + desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." + color = "#5F5F5F" + slime_color = "metal" + shiny = 1 + coretype = /obj/item/slime_extract/metal + + description_info = "This slime is a lot more durable and tough to damage than the others." + + resistance = 10 // Sloped armor is strong. + maxHealth = 250 + maxHealth_adult = 350 + + slime_mutation = list( + /mob/living/simple_animal/slime/silver, + /mob/living/simple_animal/slime/yellow, + /mob/living/simple_animal/slime/gold, + /mob/living/simple_animal/slime + ) + +// Tier 2 + +/mob/living/simple_animal/slime/yellow + desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." + color = "#FFF423" + slime_color = "yellow" + coretype = /obj/item/slime_extract/yellow + + ranged = 1 + shoot_range = 3 + firing_lines = 1 + projectiletype = /obj/item/projectile/beam/lightning/slime + projectilesound = 'sound/weapons/gauss_shoot.ogg' // Closest thing to a 'thunderstrike' sound we have. + glows = TRUE + + description_info = "This slime will fire lightning attacks at enemies if they are at range, and generate electricity \ + for their stun attack faster than usual. Insulative or reflective armor can protect from the lightning." + + slime_mutation = list( + /mob/living/simple_animal/slime/bluespace, + /mob/living/simple_animal/slime/bluespace, + /mob/living/simple_animal/slime/metal, + /mob/living/simple_animal/slime/orange + ) + +/mob/living/simple_animal/slime/yellow/handle_regular_status_updates() + if(stat == CONSCIOUS) + if(prob(25)) + power_charge = between(0, power_charge + 1, 10) + ..() + +/obj/item/projectile/beam/lightning/slime + power = 15 + +/mob/living/simple_animal/slime/yellow/ClosestDistance() // Needed or else they won't eat monkeys outside of melee range. + if(target_mob && ishuman(target_mob)) + var/mob/living/carbon/human/H = target_mob + if(istype(H.species, /datum/species/monkey)) + return 1 + return ..() + + +/mob/living/simple_animal/slime/dark_purple + desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." + color = "#660088" + slime_color = "dark purple" + coretype = /obj/item/slime_extract/dark_purple + reagent_injected = "phoron" + + description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ + If hit with a burning attack, it will erupt in flames." + + slime_mutation = list( + /mob/living/simple_animal/slime/purple, + /mob/living/simple_animal/slime/orange, + /mob/living/simple_animal/slime/ruby, + /mob/living/simple_animal/slime/ruby + ) + +/mob/living/simple_animal/slime/dark_purple/proc/ignite() + visible_message("\The [src] erupts in an inferno!") + for(var/turf/simulated/target_turf in view(2, src)) + target_turf.assume_gas("phoron", 30, 1500+T0C) + spawn(0) + target_turf.hotspot_expose(1500+T0C, 400) + qdel(src) + +/mob/living/simple_animal/slime/dark_purple/ex_act(severity) + log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") + ignite() + +/mob/living/simple_animal/slime/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] ignited due to exposure to fire.") + ignite() + +/mob/living/simple_animal/slime/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + ignite() + else + ..() + +/mob/living/simple_animal/slime/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") + ignite() + else + ..() + + + + +/mob/living/simple_animal/slime/dark_blue + desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." + color = "#2398FF" + glows = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + + description_info = "This slime is immune to the cold, however water will still kill it. A winter coat or other cold-resistant clothing can protect from the chilling aura." + + slime_mutation = list( + /mob/living/simple_animal/slime/purple, + /mob/living/simple_animal/slime/blue, + /mob/living/simple_animal/slime/cerulean, + /mob/living/simple_animal/slime/cerulean + ) + + minbodytemp = 0 + cold_damage_per_tick = 0 + +/mob/living/simple_animal/slime/dark_blue/Life() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_animal/slime/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(2, src)) + var/protection = L.get_cold_protection() + + if(protection < 1) + var/cold_factor = abs(protection - 1) + var/delta = -20 + delta *= cold_factor + L.bodytemperature = max(50, L.bodytemperature + delta) + var/turf/T = get_turf(src) + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/mob/living/simple_animal/slime/dark_blue/get_cold_protection() + return 1 // This slime is immune to cold. + +// Surface variant +/mob/living/simple_animal/slime/dark_blue/feral + name = "feral slime" + desc = "The result of slimes escaping containment from some xenobiology lab. The slime makes other entities near it feel much colder, \ + and it is more resilient to the cold. These qualities have made this color of slime able to thrive on a harsh, cold world and is able to rival \ + the ferocity of other apex predators in this region of Sif. As such, it is a very invasive species." + description_info = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It also has learned advanced combat tactics from \ + having to endure the harsh world outside its lab. Note that processing this large slime will give six cores." + icon_scale_x = 2 + icon_scale_y = 2 + optimal_combat = TRUE // Gotta be sharp to survive out there. + rabid = TRUE + rainbow_core_candidate = FALSE + cores = 6 + maxHealth = 150 + maxHealth_adult = 250 + type_on_death = /mob/living/simple_animal/slime/dark_blue // Otherwise infinite slimes might occur. + pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. + +/mob/living/simple_animal/slime/dark_blue/feral/New() + ..() + make_adult() + +/mob/living/simple_animal/slime/silver + desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." + color = "#AAAAAA" + slime_color = "silver" + coretype = /obj/item/slime_extract/silver + shiny = TRUE + + description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." + + slime_mutation = list( + /mob/living/simple_animal/slime/metal, + /mob/living/simple_animal/slime/blue, + /mob/living/simple_animal/slime/amber, + /mob/living/simple_animal/slime/amber + ) + +/mob/living/simple_animal/slime/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) + visible_message("\The [src] reflects \the [P]!") + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + return PROJECTILE_CONTINUE // complete projectile permutation + else + ..() + + +// Tier 3 + +/mob/living/simple_animal/slime/bluespace + desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." + color = null + slime_color = "bluespace" + icon_state_override = "bluespace" + coretype = /obj/item/slime_extract/bluespace + + description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." + + slime_mutation = list( + /mob/living/simple_animal/slime/bluespace, + /mob/living/simple_animal/slime/bluespace, + /mob/living/simple_animal/slime/yellow, + /mob/living/simple_animal/slime/yellow + ) + + spattack_prob = 100 + spattack_min_range = 3 + spattack_max_range = 7 + var/last_tele = null // Uses world.time + var/tele_cooldown = 5 SECONDS + +/mob/living/simple_animal/slime/bluespace/ClosestDistance() // Needed or the SA AI won't ever try to teleport. + if(world.time > last_tele + tele_cooldown) + return spattack_max_range - 1 + return ..() + +/mob/living/simple_animal/slime/bluespace/SpecialAtkTarget() + // Teleport attack. + if(!target_mob) + to_chat(src, "There's nothing to teleport to.") + return FALSE + + if(world.time < last_tele + tele_cooldown) + to_chat(src, "You can't teleport right now, wait a few seconds.") + return FALSE + + var/list/nearby_things = range(1, target_mob) + var/list/valid_turfs = list() + + // All this work to just go to a non-dense tile. + for(var/turf/potential_turf in nearby_things) + var/valid_turf = TRUE + if(potential_turf.density) + continue + for(var/atom/movable/AM in potential_turf) + if(AM.density) + valid_turf = FALSE + if(valid_turf) + valid_turfs.Add(potential_turf) + + + + var/turf/T = get_turf(src) + var/turf/target_turf = pick(valid_turfs) + + if(!target_turf) + to_chat(src, "There wasn't an unoccupied spot to teleport to.") + return FALSE + + var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread + s1.set_up(5, 1, T) + var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread + s2.set_up(5, 1, target_turf) + + + T.visible_message("\The [src] vanishes!") + s1.start() + + forceMove(target_turf) + playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) + to_chat(src, "You teleport to \the [target_turf].") + + target_turf.visible_message("\The [src] appears!") + s2.start() + + last_tele = world.time + + if(Adjacent(target_mob)) + PunchTarget() + return TRUE + +/mob/living/simple_animal/slime/ruby + desc = "This slime has great physical strength." + color = "#FF3333" + slime_color = "ruby" + shiny = TRUE + glows = TRUE + coretype = /obj/item/slime_extract/ruby + + description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ + Their glomp attacks also send the victim flying." + + slime_mutation = list( + /mob/living/simple_animal/slime/dark_purple, + /mob/living/simple_animal/slime/dark_purple, + /mob/living/simple_animal/slime/ruby, + /mob/living/simple_animal/slime/ruby + ) + +/mob/living/simple_animal/slime/ruby/New() + ..() + add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. + +/mob/living/simple_animal/slime/ruby/DoPunch(var/mob/living/L) + ..() // Do regular attacks. + + if(istype(L)) + if(a_intent == I_HURT) + visible_message("\The [src] sends \the [L] flying with the impact!") + playsound(src, "punch", 50, 1) + L.Weaken(1) + var/throwdir = get_dir(src, L) + L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) + + +/mob/living/simple_animal/slime/amber + desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ + They would probably be very important to other slimes, if the other colors didn't try to kill them." + color = "#FFBB00" + slime_color = "amber" + shiny = TRUE + glows = TRUE + coretype = /obj/item/slime_extract/amber + + description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ + slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." + + slime_mutation = list( + /mob/living/simple_animal/slime/silver, + /mob/living/simple_animal/slime/silver, + /mob/living/simple_animal/slime/amber, + /mob/living/simple_animal/slime/amber + ) + +/mob/living/simple_animal/slime/amber/Life() + if(stat != DEAD) + feed_aura() + ..() + +/mob/living/simple_animal/slime/amber/proc/feed_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. + continue + if(isslime(L)) + var/mob/living/simple_animal/slime/S = L + S.adjust_nutrition(rand(15, 25)) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic()) + continue + H.nutrition = between(0, H.nutrition + rand(15, 25), 600) + + + +/mob/living/simple_animal/slime/cerulean + desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." + color = "#4F7EAA" + slime_color = "cerulean" + coretype = /obj/item/slime_extract/cerulean + + // Less than the specialized slimes, but higher than the rest. + maxHealth = 200 + maxHealth_adult = 250 + + melee_damage_lower = 10 + melee_damage_upper = 30 + + move_to_delay = 3 + + + + slime_mutation = list( + /mob/living/simple_animal/slime/dark_blue, + /mob/living/simple_animal/slime/dark_blue, + /mob/living/simple_animal/slime/cerulean, + /mob/living/simple_animal/slime/cerulean + ) + +// Tier 4 + +/mob/living/simple_animal/slime/red + desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." + color = "#FF3333" + slime_color = "red" + coretype = /obj/item/slime_extract/red + move_to_delay = 3 // The red ones go faster. + + description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go berserk." + + slime_mutation = list( + /mob/living/simple_animal/slime/red, + /mob/living/simple_animal/slime/oil, + /mob/living/simple_animal/slime/oil, + /mob/living/simple_animal/slime/orange + ) + + +/mob/living/simple_animal/slime/red/adjust_discipline(amount) + if(amount > 0) + if(!rabid) + enrage() // How dare you try to control the red slime. + say("Grrr...!") + +/mob/living/simple_animal/slime/red/enrage() + ..() + add_modifier(/datum/modifier/berserk, 30 SECONDS) + + +/mob/living/simple_animal/slime/green + desc = "This slime is radioactive." + color = "#14FF20" + slime_color = "green" + coretype = /obj/item/slime_extract/green + glows = TRUE + reagent_injected = "radium" + var/rads = 25 + + description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ + A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." + + slime_mutation = list( + /mob/living/simple_animal/slime/purple, + /mob/living/simple_animal/slime/green, + /mob/living/simple_animal/slime/emerald, + /mob/living/simple_animal/slime/emerald + ) + +/mob/living/simple_animal/slime/green/Life() + if(stat != DEAD) + irradiate() + ..() + +/mob/living/simple_animal/slime/green/proc/irradiate() + SSradiation.radiate(src, rads) + + +/mob/living/simple_animal/slime/pink + desc = "This slime has regenerative properties." + color = "#FF0080" + slime_color = "pink" + coretype = /obj/item/slime_extract/pink + glows = TRUE + + description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." + + slime_mutation = list( + /mob/living/simple_animal/slime/blue, + /mob/living/simple_animal/slime/light_pink, + /mob/living/simple_animal/slime/light_pink, + /mob/living/simple_animal/slime/pink + ) + +/mob/living/simple_animal/slime/pink/Life() + if(stat != DEAD) + heal_aura() + ..() + +/mob/living/simple_animal/slime/pink/proc/heal_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || L == target_mob) + continue + L.add_modifier(/datum/modifier/slime_heal, 5 SECONDS, src) + +/datum/modifier/slime_heal + name = "slime mending" + desc = "You feel somewhat gooy." + mob_overlay_state = "pink_sparkles" + + on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." + on_expired_text = "The spores of goo have faded, although you feel much healthier than before." + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/slime_heal/tick() + if(holder.stat == DEAD) // Required or else simple animals become immortal. + expire() + + if(ishuman(holder)) // Robolimbs need this code sadly. + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/E in H.organs) + var/obj/item/organ/external/O = E + O.heal_damage(2, 2, 0, 1) + else + holder.adjustBruteLoss(-2) + holder.adjustFireLoss(-2) + + holder.adjustToxLoss(-2) + holder.adjustOxyLoss(-2) + holder.adjustCloneLoss(-1) + + + +/mob/living/simple_animal/slime/gold + desc = "This slime absorbs energy, and cannot be stunned by normal means." + color = "#EEAA00" + shiny = TRUE + slime_color = "gold" + coretype = /obj/item/slime_extract/gold + description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." + + slime_mutation = list( + /mob/living/simple_animal/slime/metal, + /mob/living/simple_animal/slime/gold, + /mob/living/simple_animal/slime/sapphire, + /mob/living/simple_animal/slime/sapphire + ) + +/mob/living/simple_animal/slime/gold/Weaken(amount) + power_charge = between(0, power_charge + amount, 10) + return + +/mob/living/simple_animal/slime/gold/Stun(amount) + power_charge = between(0, power_charge + amount, 10) + return + +/mob/living/simple_animal/slime/gold/get_description_interaction() // So it doesn't say to use a baton on them. + return list() + + +// Tier 5 + +/mob/living/simple_animal/slime/oil + desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." + color = "#333333" + slime_color = "oil" + shiny = TRUE + coretype = /obj/item/slime_extract/oil + + description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ + an explosion, it will explode. Rabid oil slimes will charge at enemies, then suicide-bomb themselves. \ + Bomb suits can protect from the explosion." + + slime_mutation = list( + /mob/living/simple_animal/slime/oil, + /mob/living/simple_animal/slime/oil, + /mob/living/simple_animal/slime/red, + /mob/living/simple_animal/slime/red + ) + +/mob/living/simple_animal/slime/oil/proc/explode() + if(stat != DEAD) + // explosion(src.loc, 1, 2, 4) + explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. + if(src) // Delete ourselves if the explosion didn't do it. + qdel(src) + +/mob/living/simple_animal/slime/oil/post_attack(var/mob/living/L, var/intent = I_HURT) + if(!rabid) + return ..() + if(intent == I_HURT || intent == I_GRAB) + say(pick("Sacrifice...!", "Sssss...", "Boom...!")) + sleep(2 SECOND) + log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") + explode() + +/mob/living/simple_animal/slime/oil/ex_act(severity) + log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") + explode() + +/mob/living/simple_animal/slime/oil/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] exploded due to exposure to fire.") + explode() + +/mob/living/simple_animal/slime/oil/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + explode() + else + ..() + +/mob/living/simple_animal/slime/oil/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") + explode() + else + ..() + + +/mob/living/simple_animal/slime/sapphire + desc = "This slime seems a bit brighter than the rest, both figuratively and literally." + color = "#2398FF" + slime_color = "sapphire" + shiny = TRUE + glows = TRUE + coretype = /obj/item/slime_extract/sapphire + + optimal_combat = TRUE // Lift combat AI restrictions to look smarter. + run_at_them = FALSE // Use fancy A* pathing. + astar_adjacent_proc = /turf/proc/TurfsWithAccess // Normal slimes don't care about cardinals (because BYOND) so smart slimes shouldn't as well. + move_to_delay = 3 // A* chasing is slightly slower in terms of movement speed than regular pathing so reducing this hopefully makes up for that. + + description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ + with one if hostile, and especially dangerous if they outnumber you." + + slime_mutation = list( + /mob/living/simple_animal/slime/sapphire, + /mob/living/simple_animal/slime/sapphire, + /mob/living/simple_animal/slime/gold, + /mob/living/simple_animal/slime/gold + ) + +/mob/living/simple_animal/slime/emerald + desc = "This slime is faster than usual, even more so than the red slimes." + color = "#22FF22" + shiny = TRUE + glows = TRUE + slime_color = "emerald" + coretype = /obj/item/slime_extract/emerald + + description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." + move_to_delay = 2 + + slime_mutation = list( + /mob/living/simple_animal/slime/green, + /mob/living/simple_animal/slime/green, + /mob/living/simple_animal/slime/emerald, + /mob/living/simple_animal/slime/emerald + ) + +/mob/living/simple_animal/slime/emerald/Life() + if(stat != DEAD) + zoom_aura() + ..() + +/mob/living/simple_animal/slime/emerald/proc/zoom_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || L == target_mob) + continue + L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) + +/mob/living/simple_animal/slime/light_pink + desc = "This slime seems a lot more peaceful than the others." + color = "#FF8888" + slime_color = "light pink" + coretype = /obj/item/slime_extract/light_pink + + description_info = "This slime is effectively always disciplined initially." + obedience = 5 + discipline = 5 + + slime_mutation = list( + /mob/living/simple_animal/slime/pink, + /mob/living/simple_animal/slime/pink, + /mob/living/simple_animal/slime/light_pink, + /mob/living/simple_animal/slime/light_pink + ) + +// Special +/mob/living/simple_animal/slime/rainbow + desc = "This slime changes colors constantly." + color = null // Only slime subtype that uses a different icon_state. + slime_color = "rainbow" + coretype = /obj/item/slime_extract/rainbow + icon_state_override = "rainbow" + unity = TRUE + + description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ + other slimes being friendly to them, and therefore will never be harmed by another slime. \ + Attacking this slime will provoke the wrath of all slimes within range." + + slime_mutation = list( + /mob/living/simple_animal/slime/rainbow, + /mob/living/simple_animal/slime/rainbow, + /mob/living/simple_animal/slime/rainbow, + /mob/living/simple_animal/slime/rainbow + ) + +/mob/living/simple_animal/slime/rainbow/New() + unify() + ..() + +// The RD's pet slime. +/mob/living/simple_animal/slime/rainbow/kendrick + name = "Kendrick" + desc = "The Research Director's pet slime. It shifts colors constantly." + rainbow_core_candidate = FALSE + +/mob/living/simple_animal/slime/rainbow/kendrick/New() + pacify() + ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm index 4c063e4bd9..d4ed684deb 100644 --- a/code/modules/mob/living/simple_mob/life.dm +++ b/code/modules/mob/living/simple_mob/life.dm @@ -88,7 +88,7 @@ if(Environment) - if( abs(Environment.temperature - bodytemperature) > 40 ) + if( abs(Environment.temperature - bodytemperature) > temperature_range ) //VOREStation Edit: heating adjustments bodytemperature += ((Environment.temperature - bodytemperature) / 5) if(min_oxy) diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index 0cd734ec06..c70094d579 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -47,6 +47,7 @@ var/movement_sound = null // If set, will play this sound when it moves on its own will. var/turn_sound = null // If set, plays the sound when the mob's dir changes in most cases. var/movement_shake_radius = 0 // If set, moving will shake the camera of all living mobs within this radius slightly. + var/aquatic_movement = 0 // If set, the mob will move through fluids with no hinderance. //Mob interaction var/response_help = "tries to help" // If clicked on help intent @@ -230,8 +231,11 @@ // Turf related slowdown var/turf/T = get_turf(src) - if(T && T.movement_cost && !hovering) // Flying mobs ignore turf-based slowdown. - tally += T.movement_cost + if(T && T.movement_cost && !hovering) // Flying mobs ignore turf-based slowdown. Aquatic mobs ignore water slowdown, and can gain bonus speed in it. + if(istype(T,/turf/simulated/floor/water) && aquatic_movement) + tally -= aquatic_movement - 1 + else + tally += T.movement_cost if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move. if(tally <= 0) diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm index d388d1ec88..d596b7a502 100644 --- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm +++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm @@ -1,9 +1,13 @@ // Flags for specifying which states we have vore icon_states for. #define SA_ICON_LIVING 0x01 #define SA_ICON_DEAD 0x02 -#define SA_ICON_REST 0x03 +#define SA_ICON_REST 0x04 /mob/living/simple_mob + base_attack_cooldown = 15 + + var/temperature_range = 40 // How close will they get to environmental temperature before their body stops changing its heat + var/vore_active = 0 // If vore behavior is enabled for this mob var/vore_capacity = 1 // The capacity (in people) this person can hold @@ -128,9 +132,9 @@ /mob/living/simple_mob/proc/CanPounceTarget(var/mob/living/M) //returns either FALSE or a %chance of success if(!M.canmove || issilicon(M) || world.time < vore_pounce_cooldown) //eliminate situations where pouncing CANNOT happen return FALSE - if(!prob(vore_pounce_chance)) //mob doesn't want to pounce + if(!prob(vore_pounce_chance) || !will_eat(M)) //mob doesn't want to pounce return FALSE - if(will_eat(M) && vore_standing_too) //100% chance of hitting people we can eat on the spot + if(vore_standing_too) //100% chance of hitting people we can eat on the spot return 100 var/TargetHealthPercent = (M.health/M.getMaxHealth())*100 //now we start looking at the target itself if (TargetHealthPercent > vore_pounce_maxhealth) //target is too healthy to pounce diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm index b5b4489f57..6ea44df66d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -1,237 +1,261 @@ -// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. -// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would -// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. - -/mob/living/simple_mob/animal/borer - name = "cortical borer" - desc = "A small, quivering sluglike creature." - icon_state = "brainslug" - item_state = "brainslug" - icon_living = "brainslug" - icon_dead = "brainslug_dead" - - response_help = "pokes" - response_disarm = "prods" - response_harm = "stomps on" - attacktext = list("nipped") - friendly = list("prods") - - status_flags = CANPUSH - pass_flags = PASSTABLE - movement_cooldown = 5 - - universal_understand = TRUE - can_be_antagged = TRUE - - holder_type = /obj/item/weapon/holder/borer - ai_holder_type = null // This is player-controlled, always. - - var/chemicals = 10 // A resource used for reproduction and powers. - var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. - var/true_name = null // String used when speaking among other worms. - var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. - var/controlling = FALSE // Used in human death ceck. - var/docile = FALSE // Sugar can stop borers from acting. - var/has_reproduced = FALSE - var/roundstart = FALSE // If true, spawning won't try to pull a ghost. - var/used_dominate // world.time when the dominate power was last used. - - -/mob/living/simple_mob/animal/borer/roundstart - roundstart = TRUE - -/mob/living/simple_mob/animal/borer/Login() - ..() - if(mind) - borers.add_antagonist(mind) - -/mob/living/simple_mob/animal/borer/Initialize() - add_language("Cortical Link") - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - - true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" - - if(!roundstart) - request_player() - - return ..() - -/mob/living/simple_mob/animal/borer/handle_special() - if(host && !stat && !host.stat) - // Handle docility. - if(host.reagents.has_reagent("sugar") && !docile) - var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." - var/target = controlling ? host : src - to_chat(target, span("warning", message)) - docile = TRUE - - else if(docile) - var/message = "You shake off your lethargy as the sugar leaves your host's blood." - var/target = controlling ? host : src - to_chat(target, span("notice", message)) - docile = FALSE - - // Chem regen. - if(chemicals < 250) - chemicals++ - - // Control stuff. - if(controlling) - if(docile) - to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) - host.release_control() - return - - if(prob(5)) - host.adjustBrainLoss(0.1) - - if(prob(host.brainloss/20)) - host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") - -/mob/living/simple_mob/animal/borer/Stat() - ..() - if(client.statpanel == "Status") - statpanel("Status") - if(emergency_shuttle) - var/eta_status = emergency_shuttle.get_status_panel_eta() - if(eta_status) - stat(null, eta_status) - stat("Chemicals", chemicals) - -/mob/living/simple_mob/animal/borer/proc/detatch() - if(!host || !controlling) - return - - if(istype(host, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = host - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - if(head) - head.implants -= src - - controlling = FALSE - - host.remove_language("Cortical Link") - host.verbs -= /mob/living/carbon/proc/release_control - host.verbs -= /mob/living/carbon/proc/punish_host - host.verbs -= /mob/living/carbon/proc/spawn_larvae - - if(host_brain) - // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. - // computer_id and IP are not updated magically on their own in offline mobs -walter0o - - // This shit need to die in a phoron fire. - - // host -> self - var/h2s_id = host.computer_id - var/h2s_ip= host.lastKnownIP - host.computer_id = null - host.lastKnownIP = null - - src.ckey = host.ckey - - if(!src.computer_id) - src.computer_id = h2s_id - - if(!host_brain.lastKnownIP) - src.lastKnownIP = h2s_ip - - // brain -> host - var/b2h_id = host_brain.computer_id - var/b2h_ip= host_brain.lastKnownIP - host_brain.computer_id = null - host_brain.lastKnownIP = null - - host.ckey = host_brain.ckey - - if(!host.computer_id) - host.computer_id = b2h_id - - if(!host.lastKnownIP) - host.lastKnownIP = b2h_ip - - qdel(host_brain) - - -/mob/living/simple_mob/animal/borer/proc/leave_host() - if(!host) - return - - if(host.mind) - borers.remove_antagonist(host.mind) - - forceMove(get_turf(host)) - - reset_view(null) - machine = null - - host.reset_view(null) - host.machine = null - host = null - -/mob/living/simple_mob/animal/borer/proc/request_player() - var/datum/ghost_query/Q = new /datum/ghost_query/borer() - var/list/winner = Q.query() // This will sleep the proc for awhile. - if(winner.len) - var/mob/observer/dead/D = winner[1] - transfer_personality(D) - -/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) - if(!candidate || !candidate.mind) - return - - src.mind = candidate.mind - candidate.mind.current = src - ckey = candidate.ckey - - if(mind) - mind.assigned_role = "Cortical Borer" - mind.special_role = "Cortical Borer" - - to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ - into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ - your host and your eventual spawn safe and warm.")) - to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") - -/mob/living/simple_mob/animal/borer/cannot_use_vents() - return - -// This is awful but its literally say code. -/mob/living/simple_mob/animal/borer/say(message) - message = sanitize(message) - message = capitalize(message) - - if(!message) - return - - if(stat >= DEAD) - return say_dead(message) - else if(stat) - return - - if(client && client.prefs.muted & MUTE_IC) - to_chat(src, span("danger", "You cannot speak in IC (muted).")) - return - - if(copytext(message, 1, 2) == "*") - return emote(copytext(message, 2)) - - var/datum/language/L = parse_language(message) - if(L && L.flags & HIVEMIND) - L.broadcast(src,trim(copytext(message,3)), src.true_name) - return - - if(!host) - //TODO: have this pick a random mob within 3 tiles to speak for the borer. - to_chat(src, span("warning", "You have no host to speak to.")) - return //No host, no audible speech. - - to_chat(src, "You drop words into [host]'s mind: \"[message]\"") - to_chat(host, "Your own thoughts speak: \"[message]\"") - - for(var/mob/M in player_list) - if(istype(M, /mob/new_player)) - continue - else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") +// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. +// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would +// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. + +/mob/living/simple_mob/animal/borer + name = "cortical borer" + desc = "A small, quivering sluglike creature." + icon_state = "brainslug" + item_state = "brainslug" + icon_living = "brainslug" + icon_dead = "brainslug_dead" + + response_help = "pokes" + response_disarm = "prods" + response_harm = "stomps on" + attacktext = list("nipped") + friendly = list("prods") + + status_flags = CANPUSH + pass_flags = PASSTABLE + movement_cooldown = 5 + + universal_understand = TRUE + can_be_antagged = TRUE + + holder_type = /obj/item/weapon/holder/borer + ai_holder_type = null // This is player-controlled, always. + + var/chemicals = 10 // A resource used for reproduction and powers. + var/max_chemicals = 250 // Max of said resource. + var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. + var/true_name = null // String used when speaking among other worms. + var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. + var/controlling = FALSE // Used in human death ceck. + var/docile = FALSE // Sugar can stop borers from acting. + var/has_reproduced = FALSE + var/roundstart = FALSE // If true, spawning won't try to pull a ghost. + var/used_dominate // world.time when the dominate power was last used. + + +/mob/living/simple_mob/animal/borer/roundstart + roundstart = TRUE + +/mob/living/simple_mob/animal/borer/Login() + ..() + if(mind) + borers.add_antagonist(mind) + +/mob/living/simple_mob/animal/borer/Initialize() + add_language("Cortical Link") + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" + + if(!roundstart) + request_player() + + return ..() + +/mob/living/simple_mob/animal/borer/handle_special() + if(host && !stat && !host.stat) + // Handle docility. + if(host.reagents.has_reagent("sugar") && !docile) + var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." + var/target = controlling ? host : src + to_chat(target, span("warning", message)) + docile = TRUE + + else if(docile) + var/message = "You shake off your lethargy as the sugar leaves your host's blood." + var/target = controlling ? host : src + to_chat(target, span("notice", message)) + docile = FALSE + + // Chem regen. + if(chemicals < max_chemicals) + chemicals++ + + // Control stuff. + if(controlling) + if(docile) + to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) + host.release_control() + return + + if(prob(5)) + host.adjustBrainLoss(0.1) + + if(prob(host.brainloss/20)) + host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") + +/mob/living/simple_mob/animal/borer/Stat() + ..() + if(client.statpanel == "Status") + statpanel("Status") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + stat("Chemicals", chemicals) + +/mob/living/simple_mob/animal/borer/proc/detatch() + if(!host || !controlling) + return + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + controlling = FALSE + + host.remove_language("Cortical Link") + host.verbs -= /mob/living/carbon/proc/release_control + host.verbs -= /mob/living/carbon/proc/punish_host + host.verbs -= /mob/living/carbon/proc/spawn_larvae + + if(host_brain) + // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. + // computer_id and IP are not updated magically on their own in offline mobs -walter0o + + // This shit need to die in a phoron fire. + + // host -> self + var/h2s_id = host.computer_id + var/h2s_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + src.ckey = host.ckey + + if(!src.computer_id) + src.computer_id = h2s_id + + if(!host_brain.lastKnownIP) + src.lastKnownIP = h2s_ip + + // brain -> host + var/b2h_id = host_brain.computer_id + var/b2h_ip= host_brain.lastKnownIP + host_brain.computer_id = null + host_brain.lastKnownIP = null + + host.ckey = host_brain.ckey + + if(!host.computer_id) + host.computer_id = b2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = b2h_ip + + qdel(host_brain) + + +/mob/living/simple_mob/animal/borer/proc/leave_host() + if(!host) + return + + if(host.mind) + borers.remove_antagonist(host.mind) + + forceMove(get_turf(host)) + + reset_view(null) + machine = null + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + host.reset_view(null) + host.machine = null + host = null + +/mob/living/simple_mob/animal/borer/proc/request_player() + var/datum/ghost_query/Q = new /datum/ghost_query/borer() + var/list/winner = Q.query() // This will sleep the proc for awhile. + if(winner.len) + var/mob/observer/dead/D = winner[1] + transfer_personality(D) + +/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) + if(!candidate || !candidate.mind) + return + + src.mind = candidate.mind + candidate.mind.current = src + ckey = candidate.ckey + + if(mind) + mind.assigned_role = "Cortical Borer" + mind.special_role = "Cortical Borer" + + to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ + into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ + your host and your eventual spawn safe and warm.")) + to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") + +/mob/living/simple_mob/animal/borer/cannot_use_vents() + return + +// This is awful but its literally say code. +/mob/living/simple_mob/animal/borer/say(message) + message = sanitize(message) + message = capitalize(message) + + if(!message) + return + + if(stat >= DEAD) + return say_dead(message) + else if(stat) + return + + if(client && client.prefs.muted & MUTE_IC) + to_chat(src, span("danger", "You cannot speak in IC (muted).")) + return + + if(copytext(message, 1, 2) == "*") + return emote(copytext(message, 2)) + + var/datum/language/L = parse_language(message) + if(L && L.flags & HIVEMIND) + L.broadcast(src,trim(copytext(message,3)), src.true_name) + return + + if(!host) + if(chemicals >= 30) + to_chat(src, span("alien", "..You emit a psionic pulse with an encoded message..")) + var/list/nearby_mobs = list() + for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals)))) + if(LM == src) + continue + if(!LM.stat) + nearby_mobs += LM + var/mob/living/speaker + if(nearby_mobs.len) + speaker = input("Choose a target speaker.") as null|anything in nearby_mobs + if(speaker) + log_admin("[src.ckey]/([src]) tried to force [speaker] to say: [message]") + message_admins("[src.ckey]/([src]) tried to force [speaker] to say: [message]") + speaker.say("[message]") + return + to_chat(src, span("alien", "..But nothing heard it..")) + else + to_chat(src, span("warning", "You have no host to speak to.")) + return //No host, no audible speech. + + to_chat(src, "You drop words into [host]'s mind: \"[message]\"") + to_chat(host, "Your own thoughts speak: \"[message]\"") + + for(var/mob/M in player_list) + if(istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm index 6191680d57..b146167218 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm @@ -74,6 +74,7 @@ health = 200 pass_flags = PASSTABLE movement_cooldown = 10 + movement_sound = 'sound/effects/spider_loop.ogg' poison_resist = 0.5 see_in_dark = 10 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/giant_spider_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/giant_spider_vr.dm index 2ff8c55598..4257976efc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/giant_spider_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/giant_spider_vr.dm @@ -1,3 +1,9 @@ +/mob/living/simple_mob/animal/giant_spider/electric + base_attack_cooldown = 15 + +/mob/living/simple_mob/animal/giant_spider/webslinger + base_attack_cooldown = 15 + // Slightly placeholder, mostly to replace ion hivebots on V4 /mob/living/simple_mob/animal/giant_spider/ion desc = "Furry and green, it makes you shudder to look at it. This one has brilliant green eyes and a hint of static discharge." @@ -9,9 +15,9 @@ maxHealth = 90 health = 90 - base_attack_cooldown = 10 + base_attack_cooldown = 15 projectilesound = 'sound/weapons/taser2.ogg' - projectiletype = /obj/item/projectile/ion/small + projectiletype = /obj/item/projectile/ion/pistol melee_damage_lower = 8 melee_damage_upper = 15 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm index 6889d5ba6a..84f3d533cf 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm @@ -51,6 +51,9 @@ var/egg_type = /obj/effect/spider/eggcluster/small var/web_type = /obj/effect/spider/stickyweb/dark +/datum/ai_holder/simple_mob/melee/nurse_spider + mauling = TRUE // The nurse puts mobs into webs by attacking, so it needs to attack in crit + handle_corpse = TRUE // Lets the nurse wrap dead things /mob/living/simple_mob/animal/giant_spider/nurse/inject_poison(mob/living/L, target_zone) ..() // Inject the stoxin here. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm index 3b40147794..06d4eb4caa 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm @@ -16,6 +16,8 @@ response_harm = "stomps" friendly = "pinches" + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/crab + say_list_type = /datum/say_list/crab //COFFEE! SQUEEEEEEEEE! @@ -23,3 +25,33 @@ name = "Coffee" real_name = "Coffee" desc = "It's Coffee, the other pet!" + +// Sif! + +/datum/category_item/catalogue/fauna/sif_crab + name = "Sivian Fauna - Shelf Crab" + desc = "Classification: S Ocypode glacian\ +

      \ + A small crustacean sometimes considered a pest to Sivian fisheries, \ + as the creatures often tend to ignore non-native fish species when feeding. This \ + results in an unfortunate advantage for invasive species. \ +
      \ + Otherwise, these animals are enjoyed as a reliable source of high-grade meat." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/crab/sif + icon = 'icons/mob/fish.dmi' + tt_desc = "S Ocypode glacian" + + catalogue_data = list(/datum/category_item/catalogue/fauna/sif_crab) + +/mob/living/simple_mob/animal/passive/crab/sif/Initialize() + ..() + adjust_scale(rand(5,15) / 10) + +// Meat! + +/obj/item/weapon/reagent_containers/food/snacks/meat/crab + name = "meat" + desc = "A chunk of meat." + icon_state = "crustacean-meat" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm index bdf8853b4b..790681a3fc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm @@ -1,14 +1,27 @@ // Different types of fish! They are all subtypes of this tho +/datum/category_item/catalogue/fauna/invasive_fish + name = "Invasive Fauna - Fish" + desc = "This fish is considered an invasive species according \ + to Sivian wildlife regulations. Removal or relocation is advised." + value = CATALOGUER_REWARD_TRIVIAL + /mob/living/simple_mob/animal/passive/fish name = "fish" desc = "Its a fishy. No touchy fishy." icon = 'icons/mob/fish.dmi' + item_state = "fish" + + catalogue_data = list(/datum/category_item/catalogue/fauna/invasive_fish) mob_size = MOB_SMALL // So fish are actually underwater. plane = TURF_PLANE layer = UNDERWATER_LAYER + holder_type = /obj/item/weapon/holder/fish + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish + // By default they can be in any water turf. Subtypes might restrict to deep/shallow etc var/global/list/suitable_turf_types = list( /turf/simulated/floor/beach/water, @@ -75,3 +88,212 @@ icon_living = "koi-swim" icon_dead = "koi-dead" +/datum/category_item/catalogue/fauna/javelin + name = "Sivian Fauna - Javelin Shark" + desc = "Classification: S Cetusan minimalix\ +

      \ + A small breed of fatty shark native to the waters near the Ullran Expanse.\ + The creatures are not known to attack humans or larger animals, possibly \ + due to their size. It is speculated that they are actually scavengers, \ + as they are most commonly found near the gulf floor. \ +
      \ + The Javelin's reproductive cycle only recurs between three and four \ + Sivian years. \ +
      \ + These creatures are considered a protected species, and thus require an \ + up-to-date license to be hunted." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/javelin + name = "javelin" + tt_desc = "S Cetusan minimalix" + icon_state = "javelin-swim" + icon_living = "javelin-swim" + icon_dead = "javelin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/javelin) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif + +/datum/category_item/catalogue/fauna/icebass + name = "Sivian Fauna - Glitter Bass" + desc = "Classification: X Micropterus notius crotux\ +

      \ + Initially a genetically engineered hybrid of the common Earth bass and \ + the Sivian Rock-Fish. These were designed to deal with the invasive \ + fish species, however to their creators' dismay, they instead \ + began to form their own passive niche. \ +
      \ + Due to the brilliant reflective scales earning them their name, the \ + animals pose a specific issue for Sivian animals relying on \ + bioluminesence to aid in their hunt. \ +
      \ + Despite their beauty, they are considered an invasive species." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/icebass + name = "glitter bass" + tt_desc = "X Micropterus notius crotux" + icon_state = "sifbass-swim" + icon_living = "sifbass-swim" + icon_dead = "sifbass-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/icebass) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif + + var/max_red = 150 + var/min_red = 50 + + var/max_blue = 255 + var/min_blue = 50 + + var/max_green = 150 + var/min_green = 50 + + var/dorsal_color = "#FFFFFF" + var/belly_color = "#FFFFFF" + + var/image/dorsal_image + var/image/belly_image + +/mob/living/simple_mob/animal/passive/fish/icebass/Initialize() + ..() + dorsal_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + belly_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + update_icon() + +/mob/living/simple_mob/animal/passive/fish/icebass/update_icon() + overlays.Cut() + ..() + if(!dorsal_image) + dorsal_image = image(icon, "[icon_state]_mask-body") + if(!belly_image) + belly_image = image(icon, "[icon_state]_mask-belly") + + dorsal_image.color = dorsal_color + belly_image.color = belly_color + + overlays += dorsal_image + overlays += belly_image + +/datum/category_item/catalogue/fauna/rockfish + name = "Sivian Fauna - Rock Puffer" + desc = "Classification: S Tetraodontidae scopulix\ +

      \ + A species strangely resembling the puffer-fish of Earth. These \ + creatures do not use toxic spines to protect themselves, instead \ + utilizing an incredibly durable exoskeleton that is molded by the \ + expansion of its ventral fluid bladders. \ +
      \ + Rock Puffers or 'Rock-fish' are often host to smaller creatures which \ + maneuver their way into the gap between the fish's body and shell. \ +
      \ + The species is also capable of pulling its vibrantly colored head into \ + the safer confines of its shell, the action being utilized in their \ + attempts to find a mate." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/rockfish + name = "rock-fish" + tt_desc = "S Tetraodontidae scopulix" + icon_state = "rockfish-swim" + icon_living = "rockfish-swim" + icon_dead = "rockfish-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/rockfish) + + armor = list( + "melee" = 90, + "bullet" = 50, + "laser" = -15, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100) + + var/max_red = 255 + var/min_red = 50 + + var/max_blue = 255 + var/min_blue = 50 + + var/max_green = 255 + var/min_green = 50 + + var/head_color = "#FFFFFF" + + var/image/head_image + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif + +/mob/living/simple_mob/animal/passive/fish/rockfish/Initialize() + ..() + head_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + +/mob/living/simple_mob/animal/passive/fish/rockfish/update_icon() + overlays.Cut() + ..() + if(!head_image) + head_image = image(icon, "[icon_state]_mask") + + head_image.color = head_color + + overlays += head_image + +/datum/category_item/catalogue/fauna/solarfish + name = "Sivian Fauna - Solar Fin" + desc = "Classification: S Exocoetidae solarin\ +

      \ + An incredibly rare species of Sivian fish.\ + The solar-fin missile fish is a specialized omnivore capable of \ + catching insects or small birds venturing too close to the water's \ + surface. \ +
      \ + The glimmering fins of the solar-fin are actually biofluorescent, \ + 'charged' by the creature basking at the surface of the water, most \ + commonly by the edge of an ice-shelf, as a rapid means of cover. \ +
      \ + These creatures are considered a protected species, and thus require an \ + up-to-date license to be hunted." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/animal/passive/fish/solarfish + name = "sun-fin" + tt_desc = "S Exocoetidae solarin" + icon_state = "solarfin-swim" + icon_living = "solarfin-swim" + icon_dead = "solarfin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/solarfish) + + has_eye_glow = TRUE + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif + +/datum/category_item/catalogue/fauna/murkin + name = "Sivian Fauna - Murkfish" + desc = "Classification: S Perca lutux\ +

      \ + A small, Sivian fish most known for its bland-ness.\ +
      \ + The species is incredibly close in appearance to the Earth \ + perch, aside from its incredibly tall dorsal fin. The animals use \ + the fin to assess the wind direction while near the surface. \ +
      \ + The murkfish earns its name from the fact its dense meat tastes like mud \ + thanks to a specially formed protein, most likely an adaptation to \ + protect the species from predation." + value = CATALOGUER_REWARD_TRIVIAL + +/mob/living/simple_mob/animal/passive/fish/murkin + name = "murkin" + tt_desc = "S Perca lutux" + + icon_state = "murkin-swim" + icon_living = "murkin-swim" + icon_dead = "murkin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/murkin) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index 95af426799..74f67335fa 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -118,4 +118,4 @@ /datum/say_list/mouse speak = list("Squeek!","SQUEEK!","Squeek?") emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") \ No newline at end of file + emote_see = list("runs in a circle", "shakes", "scritches at something") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm new file mode 100644 index 0000000000..e86cfd3934 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm @@ -0,0 +1,22 @@ +/mob/living/simple_mob/animal/passive/mouse + nutrition = 20 //To prevent draining maint mice for infinite food. Low nutrition has no mechanical effect on simplemobs, so wont hurt mice themselves. + + no_vore = 1 //Mice can't eat others due to the amount of bugs caused by it. + vore_taste = "cheese" + + can_pull_size = ITEMSIZE_TINY // Rykka - Uncommented these. Not sure why they were commented out in the original Polaris files, maybe a mob rework mistake? + can_pull_mobs = MOB_PULL_NONE // Rykka - Uncommented these. Not sure why they were commented out in the original Polaris files, maybe a mob rework mistake? + + desc = "A small rodent, often seen hiding in maintenance areas and making a nuisance of itself. And stealing cheese, or annoying the chef. SQUEAK! <3" + +/mob/living/simple_mob/animal/passive/mouse/attack_hand(mob/living/hander) + if(hander.a_intent == I_HELP) //if lime intent + get_scooped(hander) //get scooped + else + ..() + +/obj/item/weapon/holder/mouse/attack_self(var/mob/U) + for(var/mob/living/simple_mob/M in src.contents) + if((I_HELP) && U.canClick()) //a little snowflakey, but makes it use the same cooldown as interacting with non-inventory objects + U.setClickCooldown(U.get_attack_speed()) //if there's a cleaner way in baycode, I'll change this + U.visible_message("[U] [M.response_help] \the [M].") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm index 5e77a57a3c..4042dd30b4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm @@ -57,7 +57,8 @@ icon_state = "commonblackbird" icon_dead = "commonblackbird-dead" tt_desc = "E Turdus merula" - icon_scale = 0.5 + icon_scale_x = 0.5 + icon_scale_y = 0.5 /mob/living/simple_mob/animal/passive/bird/azure_tit name = "azure tit" @@ -65,7 +66,8 @@ icon_state = "azuretit" icon_dead = "azuretit-dead" tt_desc = "E Cyanistes cyanus" - icon_scale = 0.5 + icon_scale_x = 0.5 + icon_scale_y = 0.5 /mob/living/simple_mob/animal/passive/bird/european_robin name = "european robin" @@ -73,7 +75,8 @@ icon_state = "europeanrobin" icon_dead = "europeanrobin-dead" tt_desc = "E Erithacus rubecula" - icon_scale = 0.5 + icon_scale_x = 0.5 + icon_scale_y = 0.5 /mob/living/simple_mob/animal/passive/bird/goldcrest name = "goldcrest" @@ -82,7 +85,8 @@ icon_state = "goldcrest" icon_dead = "goldcrest-dead" tt_desc = "E Regulus regulus" - icon_scale = 0.5 + icon_scale_x = 0.5 + icon_scale_y = 0.5 /mob/living/simple_mob/animal/passive/bird/ringneck_dove name = "ringneck dove" @@ -90,4 +94,5 @@ icon_state = "ringneckdove" icon_dead = "ringneckdove-dead" tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years. - icon_scale = 0.5 + icon_scale_x = 0.5 + icon_scale_y = 0.5 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird_vr.dm new file mode 100644 index 0000000000..1640d2ddea --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird_vr.dm @@ -0,0 +1,4 @@ +/mob/living/simple_mob/animal/passive/bird/azure_tit/tweeter + name = "Tweeter" + desc = "A beautiful little blue and white bird, if only excessively loud for no reason sometimes." + makes_dirt = FALSE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm index 3f6bba4846..0a4f254bbd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm @@ -1,147 +1,182 @@ -/mob/living/simple_mob/animal/passive/cat - name = "cat" - desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." - tt_desc = "E Felis silvestris catus" - icon_state = "cat2" - item_state = "cat2" - icon_living = "cat2" - icon_dead = "cat2_dead" - icon_rest = "cat2_rest" - - movement_cooldown = 0.5 SECONDS - - see_in_dark = 6 // Not sure if this actually works. - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - - holder_type = /obj/item/weapon/holder/cat - mob_size = MOB_SMALL - - has_langs = list("Cat") - - var/mob/living/friend = null // Our best pal, who we'll follow. Meow. - var/friend_name = null //VOREStation Edit - Lock befriending to this character - -/mob/living/simple_mob/animal/passive/cat/handle_special() - if(!stat && prob(2)) // spooky - var/mob/observer/dead/spook = locate() in range(src, 5) - if(spook) - var/turf/T = get_turf(spook) - var/list/visible = list() - for(var/obj/O in T.contents) - if(!O.invisibility && O.name) - visible += O - if(visible.len) - var/atom/A = pick(visible) - visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") - -// Instakills mice. -/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) - if(ismouse(A)) - var/mob/living/simple_mob/animal/passive/mouse/mouse = A - if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. - mouse.splat() - visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) - else - ..() - -/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) - if(L == friend) // Always be pals with our special friend. - return TRUE - - . = ..() - - if(.) // We're pals, but they might be a dirty mouse... - if(ismouse(L)) - return FALSE // Cats and mice can never get along. - -/mob/living/simple_mob/animal/passive/cat/verb/become_friends() - set name = "Become Friends" - set category = "IC" - set src in view(1) - - var/mob/living/L = usr - if(!istype(L)) - return // Fuck off ghosts. - - if(friend) - if(friend == usr) - to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) - return - else - to_chat(L, span("warning", "\The [src] ignores you.")) - return - - //VOREStation Edit Start - Adds friend_name var checks - if(!friend_name || L.real_name == friend_name) - friend = L - face_atom(L) - to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) - visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) - - if(has_AI()) - var/datum/ai_holder/AI = ai_holder - AI.set_follow(friend) - else - to_chat(L, span("notice", "[src] ignores you.")) - //VOREStation Edit End - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_mob/animal/passive/cat/runtime - name = "Runtime" - desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." - tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. - gender = FEMALE - icon_state = "cat" - item_state = "cat" - icon_living = "cat" - icon_dead = "cat_dead" - icon_rest = "cat_rest" - makes_dirt = FALSE //VOREStation edit: no more dirt - -/mob/living/simple_mob/animal/passive/cat/kitten - name = "kitten" - desc = "D'aaawwww" - icon_state = "kitten" - item_state = "kitten" - icon_living = "kitten" - icon_dead = "kitten_dead" - gender = NEUTER - holder_type = /obj/item/weapon/holder/cat/kitten //VOREStation Edit - -/mob/living/simple_mob/animal/passive/cat/kitten/Initialize() - if(gender == NEUTER) - gender = pick(MALE, FEMALE) - return ..() - -// Leaving this here for now. -/obj/item/weapon/holder/cat/fluff/bones - name = "Bones" - desc = "It's Bones! Meow." - gender = MALE - icon_state = "cat3" - -/mob/living/simple_mob/animal/passive/cat/bones - name = "Bones" - desc = "That's Bones the cat. He's a laid back, black cat. Meow." - gender = MALE - icon_state = "cat3" - item_state = "cat3" - icon_living = "cat3" - icon_dead = "cat3_dead" - icon_rest = "cat3_rest" - holder_type = /obj/item/weapon/holder/cat/fluff/bones - - -/datum/say_list/cat - speak = list("Meow!","Esp!","Purr!","HSSSSS") - emote_hear = list("meows","mews") - emote_see = list("shakes their head", "shivers") - say_maybe_target = list("Meow?","Mew?","Mao?") - say_got_target = list("MEOW!","HSSSS!","REEER!") - -// VOREStation Edit - Adds generic tactical kittens -/obj/item/weapon/holder/cat/kitten - icon_state = "kitten" - w_class = ITEMSIZE_SMALL +/mob/living/simple_mob/animal/passive/cat + name = "cat" + desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." + tt_desc = "E Felis silvestris catus" + icon_state = "cat2" + item_state = "cat2" + + movement_cooldown = 0.5 SECONDS + + see_in_dark = 6 // Not sure if this actually works. + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + holder_type = /obj/item/weapon/holder/cat + mob_size = MOB_SMALL + + has_langs = list("Cat") + + var/mob/living/friend = null // Our best pal, who we'll follow. Meow. + var/named = FALSE //have I been named yet? + var/friend_name = null //VOREStation Edit - Lock befriending to this character + +/mob/living/simple_mob/animal/passive/cat/Initialize() + icon_living = "[initial(icon_state)]" + icon_dead = "[initial(icon_state)]_dead" + icon_rest = "[initial(icon_state)]_rest" + update_icon() + return ..() + +/mob/living/simple_mob/animal/passive/cat/handle_special() + if(!stat && prob(2)) // spooky + var/mob/observer/dead/spook = locate() in range(src, 5) + if(spook) + var/turf/T = get_turf(spook) + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + +// Instakills mice. +/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) + if(ismouse(A)) + var/mob/living/simple_mob/animal/passive/mouse/mouse = A + if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. + mouse.splat() + visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) + else + ..() + +/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) + if(L == friend) // Always be pals with our special friend. + return TRUE + + . = ..() + + if(.) // We're pals, but they might be a dirty mouse... + if(ismouse(L)) + return FALSE // Cats and mice can never get along. + +/mob/living/simple_mob/animal/passive/cat/verb/become_friends() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + var/mob/living/L = usr + if(!istype(L)) + return // Fuck off ghosts. + + if(friend) + if(friend == usr) + to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) + return + else + to_chat(L, span("warning", "\The [src] ignores you.")) + return + + //VOREStation Edit Start - Adds friend_name var checks + if(!friend_name || L.real_name == friend_name) + friend = L + face_atom(L) + to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) + visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) + + if(has_AI()) + var/datum/ai_holder/AI = ai_holder + AI.set_follow(friend) + else + to_chat(L, span("notice", "[src] ignores you.")) + //VOREStation Edit End + + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_mob/animal/passive/cat/runtime + name = "Runtime" + desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." + tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. + gender = FEMALE + icon_state = "cat" + item_state = "cat" + named = TRUE + makes_dirt = 0 //Vorestation Edit + +/mob/living/simple_mob/animal/passive/cat/kitten + name = "kitten" + desc = "D'aaawwww!" + icon_state = "kitten" + item_state = "kitten" + gender = NEUTER + holder_type = /obj/item/weapon/holder/cat/kitten //VOREStation Edit + +/mob/living/simple_mob/animal/passive/cat/kitten/Initialize() + if(gender == NEUTER) + gender = pick(MALE, FEMALE) + return ..() + +/mob/living/simple_mob/animal/passive/cat/black + icon_state = "cat" + item_state = "cat" + +// Leaving this here for now. +/obj/item/weapon/holder/cat/fluff/bones + name = "Bones" + desc = "It's Bones! Meow." + gender = MALE + icon_state = "cat3" + +/mob/living/simple_mob/animal/passive/cat/bones + name = "Bones" + desc = "That's Bones the cat. He's a laid back, black cat. Meow." + gender = MALE + icon_state = "cat3" + item_state = "cat3" + named = TRUE + holder_type = /obj/item/weapon/holder/cat/fluff/bones + +// VOREStation Edit - Adds generic tactical kittens +/obj/item/weapon/holder/cat/kitten + icon_state = "kitten" + w_class = ITEMSIZE_SMALL + +/datum/say_list/cat + speak = list("Meow!","Esp!","Purr!","HSSSSS") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + say_maybe_target = list("Meow?","Mew?","Mao?") + say_got_target = list("MEOW!","HSSSS!","REEER!") + +/mob/living/simple_mob/animal/passive/cat/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) + if(named) + to_chat(user, "\the [name] already has a name!") + else + var/tmp_name = sanitizeSafe(input(user, "Give \the [name] a name", "Name"), MAX_NAME_LEN) + if(length(tmp_name) > 50) + to_chat(user, "The name can be at most 50 characters long.") + else + to_chat(user, "You name \the [name]. Meow!") + name = tmp_name + named = TRUE + else + ..() + +/obj/item/weapon/cat_box + name = "faintly purring box" + desc = "This box is purring faintly. You're pretty sure there's a cat inside it." + icon = 'icons/obj/storage.dmi' + icon_state = "box" + var/cattype = /mob/living/simple_mob/animal/passive/cat + +/obj/item/weapon/cat_box/attack_self(var/mob/user) + var/turf/catturf = get_turf(src) + to_chat(user, "You peek into \the [name]-- and a cat jumps out!") + new cattype(catturf) + new /obj/item/stack/material/cardboard(catturf) //if i fits i sits + qdel(src) + +/obj/item/weapon/cat_box/black + cattype = /mob/living/simple_mob/animal/passive/cat/black diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm index c7fc27e56e..a63140ac8a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm @@ -44,8 +44,6 @@ icon = 'icons/mob/custom_items_mob.dmi' icon_state = "kitten" item_state = "kitten" - icon_living = "kitten" - icon_dead = "kitten" //Teleports out gender = MALE holder_type = /obj/item/weapon/holder/cat/fluff/tabiranth friend_name = "Ascian" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm new file mode 100644 index 0000000000..0aa74f9e6b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm @@ -0,0 +1,58 @@ +// Crystal-feather "ducks" are rather weak, but will become aggressive if you have food. + +/datum/category_item/catalogue/fauna/crystalduck + name = "Sivian Fauna - Crystal-Feather Duck" + desc = "Classification: S Anatidae vitriae \ +

      \ + Small, asocial omnivores with glistening, razor-thin feathers valued for their use as a reflective glitter by poachers. \ + The Crystal-Feather Duck commonly forms at most a familial group of four, a male, two females, and a single 'chosen' young. Primarily detrivorous browsers, \ + supplementing their diet with animals living below thinner shore ice-sheets. \ + Family units have been observed to form gangs and scavenge from Sivian domeciles and \ + various food transports during stressful months. \ + It is advised to seal and hide any form of food near even lone individuals, as they will become \ + increasingly aggressive." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/sif/duck + name = "crystal-feather duck" + desc = "A glittering flightless bird." + tt_desc = "S Anatidae vitriae" + catalogue_data = list(/datum/category_item/catalogue/fauna/crystalduck) + + faction = "duck" + + icon_state = "duck" + icon_living = "duck" + icon_dead = "duck_dead" + icon = 'icons/mob/animal.dmi' + has_eye_glow = TRUE + + maxHealth = 50 + health = 50 + + movement_cooldown = 0 + + melee_damage_lower = 2 + melee_damage_upper = 10 + base_attack_cooldown = 1 SECOND + attack_edge = 1 // Razor-edged wings, and 'claws' made for digging through ice. + attacktext = list("nipped", "bit", "cut", "clawed") + + say_list_type = /datum/say_list/duck + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative + +/datum/say_list/duck + speak = list("Wack!", "Wock?", "Wack.") + emote_see = list("ruffles its wings","looks around", "preens itself") + emote_hear = list("quacks", "giggles") + +/mob/living/simple_mob/animal/sif/duck/IIsAlly(mob/living/L) + . = ..() + + var/has_food = FALSE + for(var/obj/item/I in L.get_contents()) // Do they have food? + if(istype(I, /obj/item/weapon/reagent_containers/food)) + has_food = TRUE + break + if(has_food) // Yes? Gimme the food. + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/fluffy_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/fluffy_vr.dm index c821cf4a56..298cdffd84 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/fluffy_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/fluffy_vr.dm @@ -14,10 +14,12 @@ health = 20 movement_cooldown = 5 + makes_dirt = 0 see_in_dark = 5 mob_size = MOB_TINY makes_dirt = FALSE // No more dirt + mob_bump_flag = 0 response_help = "scritches" response_disarm = "bops" @@ -37,3 +39,12 @@ speak = list("Squee","Arf arf","Awoo","Squeak") emote_hear = list("howls","squeals","squeaks", "barks") emote_see = list("puffs its fur out", "shakes its fur", "stares directly at you") + +/mob/living/simple_mob/animal/sif/fluffy/silky + name = "Silky" + desc = "It's a blue Diyaab! It seems to be very tame and quiet." + + icon_state = "diyaab" + icon_living = "diyaab" + icon_dead = "diyaab_dead" + icon = 'icons/jungle.dmi' \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm new file mode 100644 index 0000000000..5711868d15 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm @@ -0,0 +1,169 @@ +// Frostflies are large, flightless insects with glittering wings, used as a means of deploying their gaseous self-defense mechanism. +/datum/category_item/catalogue/fauna/frostfly + name = "Sivian Fauna - Frostfly" + desc = "Classification: S Carabidae glacios \ +

      \ + A large, flightless insectoid with bioluminescent wings. \ + Frostflies utilize their vestigial wings as a method of dispersing a chemical that produces a rapid \ + endothermic reaction on contact with the air, resulting in the flash-freezing of nearby materials. \ +
      \ + Carnivorous in nature, they use their cryogenic compounds to trap smaller prey, or frighten predators. \ + Individuals are known to slalom when facing other creatures, dispersing clouds of gas, and spitting \ + condensed globs of the compound. These masses of mucous and ice seem to be intended to impede movement. \ +
      \ + Travelers are advised to avoid frostfly swarms whenever possible, as they will become aggressive \ + to anything other than Diyaabs, which they seem to have formed a tangential symbiosis with." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/animal/sif/frostfly + name = "frostfly" + desc = "A large insect with glittering wings." + tt_desc = "S Carabidae glacios" + catalogue_data = list(/datum/category_item/catalogue/fauna/frostfly) + + faction = "diyaab" + + icon_state = "firefly" + icon_living = "firefly" + icon_dead = "firefly_dead" + icon_rest = "firefly_dead" + icon = 'icons/mob/animal.dmi' + has_eye_glow = TRUE + + maxHealth = 65 + health = 65 + + pass_flags = PASSTABLE + + var/energy = 100 + var/max_energy = 100 + + movement_cooldown = 0.5 + + melee_damage_lower = 5 + melee_damage_upper = 10 + base_attack_cooldown = 1.5 SECONDS + attacktext = list("nipped", "bit", "pinched") + + projectiletype = /obj/item/projectile/energy/blob/freezing + + special_attack_cooldown = 5 SECONDS + special_attack_min_range = 0 + special_attack_max_range = 4 + + armor = list( + "melee" = 20, + "bullet" = 10, + "laser" = 5, + "energy" = 0, + "bomb" = 10, + "bio" = 100, + "rad" = 100 + ) + + // The frostfly's body is incredibly cold at all times, natural resistance to things trying to burn it. + armor_soak = list( + "melee" = 0, + "bullet" = 0, + "laser" = 15, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + var/datum/effect/effect/system/smoke_spread/frost/smoke_special + + say_list_type = /datum/say_list/frostfly + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/frostfly + +/mob/living/simple_mob/animal/sif/frostfly/get_cold_protection() + return 1 // It literally produces a cryogenic mist inside itself. Cold doesn't bother it. + +/mob/living/simple_mob/animal/sif/frostfly/Initialize() + ..() + smoke_special = new + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + +/datum/say_list/frostfly + speak = list("Zzzz.", "Kss.", "Zzt?") + emote_see = list("flutters its wings","looks around", "rubs its mandibles") + emote_hear = list("chitters", "clicks", "chirps") + + say_understood = list("Ssst.") + say_cannot = list("Zzrt.") + say_maybe_target = list("Ki?") + say_got_target = list("Ksst!") + say_threaten = list("Kszsz.","Kszzt...","Kzzi!") + say_stand_down = list("Sss.","Zt.","! clicks.") + say_escalate = list("Rszt!") + + threaten_sound = 'sound/effects/refill.ogg' + stand_down_sound = 'sound/effects/sparks5.ogg' + +/mob/living/simple_mob/animal/sif/frostfly/handle_special() + ..() + + if(energy < max_energy) + energy++ + +/mob/living/simple_mob/animal/sif/frostfly/Stat() + ..() + if(client.statpanel == "Status") + statpanel("Status") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + stat("Energy", energy) + +/mob/living/simple_mob/animal/sif/frostfly/should_special_attack(atom/A) + if(energy >= 20) + return TRUE + return FALSE + +/mob/living/simple_mob/animal/sif/frostfly/do_special_attack(atom/A) + . = TRUE + switch(a_intent) + if(I_DISARM) + if(energy < 20) + return FALSE + + energy -= 20 + + if(smoke_special) + smoke_special.set_up(7,0,src) + smoke_special.start() + return TRUE + + return FALSE + +/datum/ai_holder/simple_mob/ranged/kiting/threatening/frostfly + can_flee = TRUE + dying_threshold = 0.5 + flee_when_outmatched = TRUE + run_if_this_close = 3 + +/datum/ai_holder/simple_mob/ranged/kiting/threatening/frostfly/special_flee_check() + var/mob/living/simple_mob/animal/sif/frostfly/F = holder + if(F.energy < F.max_energy * 0.2) + return TRUE + return FALSE + +/datum/ai_holder/simple_mob/ranged/kiting/threatening/frostfly/pre_special_attack(atom/A) + if(isliving(A)) + holder.a_intent = I_DISARM + else + holder.a_intent = I_HURT + +/datum/ai_holder/simple_mob/ranged/kiting/threatening/frostfly/post_ranged_attack(atom/A) + var/mob/living/simple_mob/animal/sif/frostfly/F = holder + if(istype(A,/mob/living)) + var/new_dir = turn(F.dir, -90) + if(prob(50)) + new_dir = turn(F.dir, 90) + holder.IMove(get_step(holder, new_dir)) + holder.face_atom(A) + + F.energy = max(0, F.energy - 1) // The AI will eventually flee. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm new file mode 100644 index 0000000000..ae1abb363f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm @@ -0,0 +1,108 @@ +// Glitterflies! Little butterfly-like creatures that float around and be cute. + +/datum/category_item/catalogue/fauna/glitterfly + name = "Sivian Fauna - Glitterfly" + desc = "Classification: S Lepidoptera adamas \ +

      \ + An herbivorous insectoid beloved for its glittering wings. \ +
      \ + The creature has four wings mirrored laterally, which reflect \ + the ambient light to provide a measure of camouflage. \ + Their wings appearance does not come free, causing them to \ + produce large amounts of reflective flake-like dandruff." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/glitterfly_rare + name = "Sivian Fauna - Adela's Glitterfly" + desc = "Classification: S Lepidoptera adamas \ +

      \ + This mutation of the glitterfly is incredibly rare, one of the few and first known individuals\ + known to exist with it is owned by little-known Sivian ecologist Adela Svingelm, and kept within its own \ + climate-controlled habitat." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/animal/sif/glitterfly + name = "glitterfly" + desc = "A large, shiny butterfly!" + description_info = "Glitterflies tend to have a wingspan equivalent to the length of an average human head." + tt_desc = "S Lepidoptera adamas" + catalogue_data = list(/datum/category_item/catalogue/fauna/glitterfly) + + faction = "neutral" + + icon_state = "butterfly" + icon_living = "butterfly" + icon_dead = "butterfly_dead" + icon = 'icons/mob/animal.dmi' + + maxHealth = 10 + health = 10 + + movement_cooldown = -1 + hovering = TRUE + + melee_damage_lower = 1 + melee_damage_upper = 2 + attack_armor_pen = 80 + attack_sharp = TRUE + + density = FALSE // Non-dense, so things can walk through their groups unhindered. + + pass_flags = PASSTABLE + + attacktext = list("bit", "buffeted", "slashed") + + say_list_type = /datum/say_list/glitterfly + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive/glitterfly + +/mob/living/simple_mob/animal/sif/glitterfly/Initialize() + ..() + var/colorlist = list(rgb(rand(100,255), rand(100,255), rand(100,255)) = 10, rgb(rand(5,100), rand(5,100), rand(5,100)) = 2, "#222222" = 1) + color = pickweight(colorlist) + + default_pixel_y = rand(5,12) + pixel_y = default_pixel_y + + adjust_scale(round(rand(90, 105) / 100)) + +/mob/living/simple_mob/animal/sif/glitterfly/rare + name = "sparkling glitterfly" + desc = "A large, incredibly shiny butterfly!" + catalogue_data = list(/datum/category_item/catalogue/fauna/glitterfly, /datum/category_item/catalogue/fauna/glitterfly_rare) + maxHealth = 30 + health = 30 + + movement_cooldown = -2 + + melee_damage_upper = 5 + + plane = PLANE_LIGHTING_ABOVE + +/mob/living/simple_mob/animal/sif/glitterfly/rare/Initialize() + ..() + +/datum/say_list/glitterfly + speak = list("Pi..","Po...", "Pa...") + emote_see = list("vibrates","flutters", "twirls") + emote_hear = list("pips", "clicks", "chirps") + +/datum/ai_holder/simple_mob/melee/evasive/glitterfly + hostile = FALSE + can_flee = TRUE + flee_when_outmatched = TRUE + outmatched_threshold = 100 + max_home_distance = 5 + +/datum/ai_holder/simple_mob/melee/evasive/glitterfly/handle_special_strategical() + if(prob(1)) + var/friendly_animal_corpse = FALSE + for(var/mob/living/simple_mob/animal/A in view(vision_range,holder)) + if(holder.IIsAlly(A) && A.stat == DEAD) + friendly_animal_corpse = TRUE + break + + if(friendly_animal_corpse) + hostile = TRUE + return + else if(prob(1)) + hostile = initial(hostile) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm index ca8288e421..5f4a4dc7e0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm @@ -32,7 +32,8 @@ icon_state = "sif_crab" icon_living = "sif_crab" icon_dead = "sif_crab_dead" - icon_scale = 1.5 + icon_scale_x = 1.5 + icon_scale_y = 1.5 faction = "crabs" @@ -71,7 +72,7 @@ attack_edge = TRUE melee_attack_delay = 1 SECOND - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/crab response_help = "pets" response_disarm = "gently pushes aside" @@ -116,4 +117,4 @@ for(var/mob/living/L in hearers(holder)) if(!istype(L, holder)) // Don't follow other hooligan crabs. holder.visible_message("\The [holder] starts to follow \the [L].") - set_follow(L, rand(20 SECONDS, 40 SECONDS)) \ No newline at end of file + set_follow(L, rand(20 SECONDS, 40 SECONDS)) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm new file mode 100644 index 0000000000..22c2d02082 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm @@ -0,0 +1,404 @@ +// Kururaks, large pack-hunting felinids that reside in coastal regions. Less slowdown in water, speed on rocky turf. + +/datum/category_item/catalogue/fauna/kururak + name = "Sivian Fauna - Kururak" + desc = "Classification: S Felidae fluctursora \ +

      \ + An uncommon sight to many Sivian residents, these creatures are hypercarnivores, with\ + their diets almost exclusively consisting of other fauna. This is achieved in the frozen \ + environments of Sif via the means of fishing, even going to the lengths of evolving a \ + third, dense lung only inflated for long dives. \ +
      \ + One of the most distinguishing features of these animals are their four tails, capped in \ + reflective 'mirrors'. These mirrors are used for tricking fish and other prey into ambushes,\ + or distract would-be rivals. \ +
      \ + Kururak packs are incredibly dangerous if faced alone, and should only be approached if prepared \ + for a fight." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/animal/sif/kururak + name = "kururak" + desc = "A large animal with sleek fur." + tt_desc = "S Felidae fluctursora" + catalogue_data = list(/datum/category_item/catalogue/fauna/kururak) + + faction = "kururak" + + icon_state = "bigcat" + icon_living = "bigcat" + icon_dead = "bigcat_dead" + icon_rest = "bigcat_rest" + icon = 'icons/mob/64x64.dmi' + + default_pixel_x = -16 + pixel_x = -16 + + maxHealth = 200 + health = 200 + + universal_understand = 1 + + movement_cooldown = 1 + + melee_damage_lower = 15 + melee_damage_upper = 25 + attack_armor_pen = 40 + base_attack_cooldown = 2 SECONDS + attacktext = list("gouged", "bit", "cut", "clawed", "whipped") + + armor = list( + "melee" = 30, + "bullet" = 15, + "laser" = 5, + "energy" = 0, + "bomb" = 10, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 5, + "bullet" = 5, + "laser" = 5, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + say_list_type = /datum/say_list/kururak + ai_holder_type = /datum/ai_holder/simple_mob/intentional/kururak + + special_attack_min_range = 0 + special_attack_max_range = 4 + special_attack_cooldown = 30 SECONDS + + // Players have 2 seperate cooldowns for these, while the AI must choose one. Both respect special_attack_cooldown + var/last_strike_time = 0 + var/last_flash_time = 0 + + var/instinct // The points used by Kururaks to decide Who Is The Boss + var/obey_pack_rule = TRUE // Decides if the Kururak will automatically assign itself to follow the one with the highest instinct. + +/datum/say_list/kururak + speak = list("Kurr?","|R|rrh..", "Ksss...") + emote_see = list("scratches its ear","flutters its tails", "flicks an ear", "shakes out its hair") + emote_hear = list("chirps", "clicks", "grumbles", "chitters") + +/mob/living/simple_mob/animal/sif/kururak/leader // Going to be the starting leader. Has some base buffs to make it more likely to stay the leader. + maxHealth = 250 + health = 250 + instinct = 50 + +/mob/living/simple_mob/animal/sif/kururak/Initialize() + ..() + if(!instinct) + if(prob(20)) + instinct = rand(6, 10) + return + instinct = rand(0, 5) + +/mob/living/simple_mob/animal/sif/kururak/IIsAlly(mob/living/L) + . = ..() + if(!.) + if(issilicon(L)) // Metal things are usually reflective, or in general aggrivating. + return FALSE + if(ishuman(L)) // Might be metal, but they're humanoid shaped. + var/mob/living/carbon/human/H = L + if(H.get_active_hand()) + var/obj/item/I = H.get_active_hand() + if(I.force >= 1.20 * melee_damage_upper) + return TRUE + else if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = L + if(S.melee_damage_upper > 1.20 * melee_damage_upper) + return TRUE + +/mob/living/simple_mob/animal/sif/kururak/handle_special() + ..() + if(client) + pack_gauge() + +/mob/living/simple_mob/animal/sif/kururak/apply_melee_effects(atom/A) // Only gains instinct. + instinct += rand(1, 2) + return + +/mob/living/simple_mob/animal/sif/kururak/should_special_attack(atom/A) + return has_modifier_of_type(/datum/modifier/ace) + +/mob/living/simple_mob/animal/sif/kururak/do_special_attack(atom/A) + . = TRUE + switch(a_intent) + if(I_DISARM) // Ranged mob flash, will also confuse borgs rather than stun. + tail_flash(A) + if(I_GRAB) // Armor-ignoring hit, causes agonizing wounds. + set_AI_busy(TRUE) + rending_strike(A) + set_AI_busy(FALSE) + +/mob/living/simple_mob/animal/sif/kururak/verb/do_flash() + set category = "Abilities" + set name = "Tail Blind" + set desc = "Disorient a creature within range." + + if(world.time < last_flash_time + special_attack_cooldown) + to_chat(src, span("warning", "You do not have the focus to do this so soon..")) + return + + last_flash_time = world.time + tail_flash() + +/mob/living/simple_mob/animal/sif/kururak/proc/tail_flash(atom/A) + set waitfor = FALSE + + if(stat) + to_chat(src, span("warning","You cannot move your tails in this state..")) + return + + if(!A && src.client) + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + for(var/obj/mecha/M in view(1,src)) + if(src.Adjacent(M)) + choices += M + + if(!choices.len) + choices["radial"] = get_turf(src) + + A = input(src,"What do we wish to flash?") in null|choices + + + visible_message(span("alien","\The [src] flares its tails!")) + if(isliving(A)) + var/mob/living/L = A + if(iscarbon(L)) + var/mob/living/carbon/C = L + if(C.stat != DEAD) + var/safety = C.eyecheck() + if(safety <= 0) + var/flash_strength = 5 + if(ishuman(C)) + var/mob/living/carbon/human/H = C + flash_strength *= H.species.flash_mod + if(flash_strength > 0) + to_chat(H, span("alien","You are disoriented by \the [src]!")) + H.Confuse(flash_strength + 5) + H.Blind(flash_strength) + H.eye_blurry = max(H.eye_blurry, flash_strength + 5) + H.flash_eyes() + H.adjustHalLoss(flash_strength / 5) + H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns") + + else if(issilicon(L)) + if(isrobot(L)) + var/flashfail = FALSE + var/mob/living/silicon/robot/R = L + if(R.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in R + if(shield) + if(shield.active) + shield.adjust_flash_count(R, 1) + flashfail = TRUE + if(!flashfail) + to_chat(R, span("alien","Your optics are scrambled by \the [src]!")) + R.Confuse(10) + R.flash_eyes() + + else + L.Confuse(10) + L.flash_eyes() + + else + for(var/mob/living/carbon/C in oviewers(special_attack_max_range, null)) + var/safety = C.eyecheck() + if(!safety) + if(!C.blinded) + C.flash_eyes() + for(var/mob/living/silicon/robot/R in oviewers(special_attack_max_range, null)) + if(R.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in R + if(shield) + if(shield.active) + continue + R.flash_eyes() + +/mob/living/simple_mob/animal/sif/kururak/verb/do_strike() + set category = "Abilities" + set name = "Rending Strike" + set desc = "Strike viciously at an entity within range." + + if(world.time < last_strike_time + special_attack_cooldown) + to_chat(src, span("warning", "Your claws cannot take that much stress in so short a time..")) + return + + last_strike_time = world.time + rending_strike() + +/mob/living/simple_mob/animal/sif/kururak/proc/rending_strike(atom/A) + if(stat) + to_chat(src, span("warning","You cannot strike in this state..")) + return + + if(!A && src.client) + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + for(var/obj/mecha/M in view(1,src)) + if(src.Adjacent(M)) + choices += M + + if(!choices.len) + to_chat(src, span("warning","There are no viable targets within range...")) + return + + A = input(src,"What do we wish to strike?") in null|choices + + if(!A || !src) return + + if(!(src.Adjacent(A))) return + + var/damage_to_apply = rand(melee_damage_lower, melee_damage_upper) + 10 + if(isliving(A)) + visible_message(span("danger","\The [src] rakes its claws across [A].")) + var/mob/living/L = A + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.apply_damage(damage_to_apply, BRUTE, BP_TORSO, 0, 0, "Animal claws") + + else + L.adjustBruteLoss(damage_to_apply) + + L.add_modifier(/datum/modifier/grievous_wounds, 60 SECONDS) + + else if(istype(A, /obj/mecha)) + visible_message(span("danger","\The [src] rakes its claws against \the [A].")) + var/obj/mecha/M = A + M.take_damage(damage_to_apply) + if(prob(3) && do_after(src, 5)) + visible_message(span("critical","\The [src]'s strike ripped \the [M]'s access hatch open, allowing it to drag [M.occupant] out!")) + M.go_out() + + else + A.attack_generic(src, damage_to_apply, "rakes its claws against") // Well it's not a mob, and it's not a mech. + +/mob/living/simple_mob/animal/sif/kururak/verb/rally_pack() // Mostly for telling other players to follow you. AI Kururaks will auto-follow, if set to. + set name = "Rally Pack" + set desc = "Tries to command your fellow pack members to follow you." + set category = "Abilities" + + if(has_modifier_of_type(/datum/modifier/ace)) + for(var/mob/living/simple_mob/animal/sif/kururak/K in hearers(7, src)) + if(K == src) + continue + if(!K.ai_holder) + continue + if(K.faction != src.faction) + continue + var/datum/ai_holder/AI = K.ai_holder + to_chat(K, span("notice","The pack leader wishes for you to follow them.")) + AI.set_follow(src) + +/mob/living/simple_mob/animal/sif/kururak/proc/detect_instinct() // Will return the Kururak within 10 tiles that has the highest instinct. + var/mob/living/simple_mob/animal/sif/kururak/A + + var/pack_count = 0 + + for(var/mob/living/simple_mob/animal/sif/kururak/K in hearers(10, src)) + if(K == src) + continue + if(K.stat != DEAD) + pack_count++ + if(K.instinct > src.instinct) + A = K + + if(!A && pack_count) + A = src + + return A + +/mob/living/simple_mob/animal/sif/kururak/proc/pack_gauge() // Check incase we have a client. + var/mob/living/simple_mob/animal/sif/kururak/highest_instinct = detect_instinct() + if(highest_instinct == src) + add_modifier(/datum/modifier/ace, 60 SECONDS) + else + remove_modifiers_of_type(/datum/modifier/ace) + +/datum/ai_holder/simple_mob/intentional/kururak + hostile = FALSE + retaliate = TRUE + cooperative = TRUE + can_flee = TRUE + flee_when_dying = TRUE + +/datum/ai_holder/simple_mob/intentional/kururak/handle_special_strategical() + follow_distance = rand(initial(follow_distance), initial(follow_distance) + 2) + var/mob/living/simple_mob/animal/sif/kururak/K = holder + + if(istype(K)) + var/mob/living/simple_mob/animal/sif/kururak/highest_instinct = K.detect_instinct() + if(highest_instinct == K) + K.add_modifier(/datum/modifier/ace, 60 SECONDS) + else + K.remove_modifiers_of_type(/datum/modifier/ace) + + if(holder.has_modifier_of_type(/datum/modifier/ace)) + if(leader && istype(leader, /mob/living/simple_mob/animal/sif/kururak)) // Kururaks will not follow another kururak if they're the pack leader. + lose_follow() + + else if(highest_instinct) + set_follow(highest_instinct) + + if(holder.has_modifier_of_type(/datum/modifier/ace)) + hostile = TRUE + else + hostile = initial(hostile) + +/datum/ai_holder/simple_mob/intentional/kururak/pre_special_attack(atom/A) + holder.a_intent = I_HURT + if(isliving(A)) + var/mob/living/L = A + if(holder.Adjacent(L)) + holder.a_intent = I_GRAB + + if(iscarbon(L)) + var/mob/living/carbon/C = L + if(!C.eyecheck()) + if(holder.a_intent != I_GRAB) + holder.a_intent = I_DISARM + + if(issilicon(L) && holder.a_intent != I_GRAB) + holder.a_intent = I_DISARM + + else if(istype(A, /obj/mecha)) + holder.a_intent = I_GRAB + +/datum/ai_holder/simple_mob/intentional/kururak/post_melee_attack() + if(holder.has_modifier_of_type(/datum/modifier/ace)) + request_help() + +// Kururak Ace modifier, given to the one with the highest Instinct. + +/datum/modifier/ace + name = "Ace" + desc = "You are universally superior, in terms of physical prowess." + on_created_text = "You feel superior." + on_expired_text = "You feel your superiority lessen..." + stacks = MODIFIER_STACK_EXTEND + + mob_overlay_state = "ace" + + max_health_flat = 25 + max_health_percent = 1.2 + disable_duration_percent = 0.8 + incoming_damage_percent = 0.7 + incoming_healing_percent = 1.5 + outgoing_melee_damage_percent = 1.5 + evasion = 20 + bleeding_rate_percent = 0.7 + attack_speed_percent = 0.8 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm new file mode 100644 index 0000000000..c59e292a4d --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm @@ -0,0 +1,503 @@ +// Small creatures that will embed themselves in unsuspecting victim's bodies, drink their blood, and/or eat their organs. Steals some things from borers. + +/datum/category_item/catalogue/fauna/iceleech + name = "Sivian Fauna - River Leech" + desc = "Classification: S Hirudinea phorus \ +

      \ + An incredibly dangerous species of worm phorogenically mutated from a Sivian river leech, \ + believed to have resulted from corporate mining in the Ullran Expanse; these accusations are \ + unfounded, however speculation remains.\ +
      \ + The creatures' heads hold four long prehensile tendrils surrounding a central beak, which are \ + used as locomotive and grappling appendages. Each is capped in a hollow tooth, capable of pumping \ + chemicals into an unsuspecting host. \ +
      \ + The rear half of the creature is entirely musculature, capped with a sharp, arrowhead-shaped fin." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/animal/sif/leech + name = "river leech" + desc = "What appears to be an oversized leech." + tt_desc = "S Hirudinea phorus" + catalogue_data = list(/datum/category_item/catalogue/fauna/iceleech) + + faction = "leech" + + icon_state = "leech" + item_state = "brainslug" + icon_living = "leech" + icon_dead = "leech_dead" + icon = 'icons/mob/animal.dmi' + + density = FALSE // Non-dense, so things can pass over them. + + status_flags = CANPUSH + pass_flags = PASSTABLE + + maxHealth = 100 + health = 100 + + universal_understand = 1 + + special_attack_min_range = 0 + special_attack_max_range = 1 + + var/obj/item/organ/external/host_bodypart // Where in the body we are infesting. + var/docile = FALSE + var/chemicals = 0 + var/max_chemicals = 400 + var/list/bodypart_targets = list(BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_TORSO,BP_GROIN,BP_HEAD) + var/infest_target = BP_TORSO // The currently chosen bodypart to infest. + var/mob/living/carbon/host // Our humble host. + var/list/produceable_chemicals = list("inaprovaline","anti_toxin","alkysine","bicaridine","tramadol","kelotane","leporazine","iron","phoron","condensedcapsaicin_v","frostoil") + var/randomized_reagent = "iron" // The reagent chosen at random to be produced, if there's no one piloting the worm. + var/passive_reagent = "paracetamol" // Reagent passively produced by the leech. Should usually be a painkiller. + + var/feeding_delay = 30 SECONDS // How long do we have to wait to bite our host's organs? + var/last_feeding = 0 + + intent = I_HELP + + holder_type = /obj/item/weapon/holder/leech + + movement_cooldown = 0 + aquatic_movement = -2 + + melee_damage_lower = 1 + melee_damage_upper = 5 + attack_armor_pen = 15 + attack_sharp = TRUE + attacktext = list("nipped", "bit", "pinched") + + armor = list( + "melee" = 10, + "bullet" = 15, + "laser" = -10, + "energy" = 0, + "bomb" = 10, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 5, + "bullet" = 5, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + say_list_type = /datum/say_list/leech + ai_holder_type = /datum/ai_holder/simple_mob/intentional/leech + +/mob/living/simple_mob/animal/sif/leech/IIsAlly(mob/living/L) + . = ..() + + var/mob/living/carbon/human/H = L + if(!istype(H)) + return . + + if(istype(L.buckled, /obj/vehicle) || L.hovering) // Ignore people hovering or on boats. + return TRUE + + if(!.) + var/has_organ = FALSE + var/obj/item/organ/internal/O = H.get_active_hand() + if(istype(O) && O.robotic < ORGAN_ROBOT && !(O.status & ORGAN_DEAD)) + has_organ = TRUE + return has_organ + +/datum/say_list/leech + speak = list("...", "Sss..", ". . .","Gss..") + emote_see = list("vibrates","looks around", "stares", "extends a proboscis") + emote_hear = list("chitters", "clicks", "gurgles") + +/mob/living/simple_mob/animal/sif/leech/Initialize() + ..() + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + +/mob/living/simple_mob/animal/sif/leech/Stat() + ..() + if(client.statpanel == "Status") + statpanel("Status") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + stat("Chemicals", chemicals) + +/mob/living/simple_mob/animal/sif/leech/do_special_attack(atom/A) + . = TRUE + if(istype(A, /mob/living/carbon)) + switch(a_intent) + if(I_DISARM) // Poison + set_AI_busy(TRUE) + poison_inject(src, A) + set_AI_busy(FALSE) + if(I_GRAB) // Infesting! + set_AI_busy(TRUE) + do_infest(src, A) + set_AI_busy(FALSE) + +/mob/living/simple_mob/animal/sif/leech/handle_special() + if(prob(5)) + randomized_reagent = pick(produceable_chemicals) + + var/turf/T = get_turf(src) + if(istype(T, /turf/simulated/floor/water) && src.loc == T && !stat) // Are we sitting in water, and alive? + alpha = max(5, alpha - 10) + if(chemicals + 1 < max_chemicals / 3) + chemicals++ + else + alpha = min(255, alpha + 20) + + if(!client && !host) + infest_target = pick(bodypart_targets) + + if(host && !stat && !host.stat) + if(ai_holder) + ai_holder.hostile = FALSE + ai_holder.lose_target() + alpha = 5 + if(host.reagents.has_reagent("cordradaxon") && !docile) // Overwhelms the leech with food. + var/message = "We feel the rush of cardiac pluripotent cells in your host's blood, lulling us into docility." + to_chat(src, span("warning", message)) + docile = TRUE + if(chemicals + 5 <= max_chemicals) + chemicals += 5 + + else if(docile) + var/message = "We shake off our lethargy as the pluripotent cell count declines in our host's blood." + to_chat(src, span("notice", message)) + docile = FALSE + + if(!host.reagents.has_reagent(passive_reagent)) + host.reagents.add_reagent(passive_reagent, 5) + chemicals -= 3 + + if(!docile && ishuman(host) && chemicals < max_chemicals) + var/mob/living/carbon/human/H = host + H.vessel.remove_reagent("blood", 1) + if(!H.reagents.has_reagent("inaprovaline")) + H.reagents.add_reagent("inaprovaline", 1) + chemicals += 2 + + if(!client && !docile) // Automatic 'AI' to manage damage levels. + if(host.getBruteLoss() >= 30 && chemicals > 50) + host.reagents.add_reagent("bicaridine", 5) + chemicals -= 30 + + if(host.getToxLoss() >= 30 && chemicals > 50) + var/randomchem = pickweight("tramadol" = 7, "anti_toxin" = 15, "frostoil" = 3) + host.reagents.add_reagent(randomchem, 5) + chemicals -= 50 + + if(host.getFireLoss() >= 30 && chemicals > 50) + host.reagents.add_reagent("kelotane", 5) + host.reagents.add_reagent("leporazine", 2) + chemicals -= 50 + + if(host.getOxyLoss() >= 30 && chemicals > 50) + host.reagents.add_reagent("iron", 10) + chemicals -= 40 + + if(host.getBrainLoss() >= 10 && chemicals > 100) + host.reagents.add_reagent("alkysine", 5) + host.reagents.add_reagent("tramadol", 3) + chemicals -= 100 + + if(prob(30) && chemicals > 50) + inject_meds(randomized_reagent) + + var/heartless_mod = 0 + if(ishuman(host)) // Species without hearts mean the worm gets hungry faster, if AI controlled. + var/mob/living/carbon/human/H = host + if(!H.species.has_organ[O_HEART]) + heartless_mod = 1 + + if(prob(15 + (20 * heartless_mod))) + feed_on_organ() + else + if(ai_holder) + ai_holder.hostile = initial(ai_holder.hostile) + + if(host && host.stat == DEAD && istype(get_turf(host), /turf/simulated/floor/water)) + leave_host() + +/mob/living/simple_mob/animal/sif/leech/verb/infest() + set category = "Abilities" + set name = "Infest" + set desc = "Infest a suitable humanoid host." + + if(docile) + to_chat(src, span("alium","We are too tired to do this...")) + return + + do_infest(usr) + +/mob/living/simple_mob/animal/sif/leech/proc/do_infest(var/mob/living/user, var/mob/living/target = null) + if(host) + to_chat(user, span("alien", "We are already within a host.")) + return + + if(stat) + to_chat(user, span("warning","We cannot infest a target in your current state.")) + return + + var/mob/living/carbon/M = target + + if(!M && src.client) + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + if(!choices.len) + to_chat(user, span("warning","There are no viable hosts within range...")) + return + + M = input(src,"Who do we wish to infest?") in null|choices + + if(!M || !src) return + + if(!(src.Adjacent(M))) return + + if(!istype(M) || M.isSynthetic()) + to_chat(user, "\The [M] cannot be infested.") + return + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + + var/obj/item/organ/external/E = H.organs_by_name[infest_target] + if(!E || E.is_stump() || E.robotic >= ORGAN_ROBOT) + to_chat(src,"\The [H] does not have an infestable [infest_target]!") + + var/list/covering_clothing = E.get_covering_clothing() + for(var/obj/item/clothing/C in covering_clothing) + if(C.armor["melee"] >= 20 + attack_armor_pen) + to_chat(user, span("notice","We cannot get through that host's protective gear.")) + return + + if(!do_after(src,2)) + to_chat(user, span("notice", "As [M] moves away, we are dislodged and fall to the ground.")) + return + + if(!M || !src) + return + + if(src.stat) + to_chat(user, span("warning","We cannot infest a target in your current state.")) + return + + if(M in view(1, src)) + to_chat(user,span("alien", "We burrow into [M]'s flesh.")) + if(!M.stat) + to_chat(M, span("critical", "You feel a sharp pain as something digs into your flesh!")) + + src.host = M + src.forceMove(M) + if(ai_holder) + ai_holder.hostile = FALSE + ai_holder.lose_target() + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + host_bodypart = H.get_organ(infest_target) + host_bodypart.implants |= src + + return + else + to_chat(user, span("notice","They are no longer in range.")) + return + +/mob/living/simple_mob/animal/sif/leech/verb/uninfest() + set category = "Abilities" + set name = "Uninfest" + set desc = "Leave your current host." + + if(docile) + to_chat(src, span("alium","We are too tired to do this...")) + return + + leave_host() + +/mob/living/simple_mob/animal/sif/leech/proc/leave_host() + if(!host) + return + + if(ai_holder) + ai_holder.hostile = initial(ai_holder.hostile) + ai_holder.lose_target() + + host_bodypart.implants -= src + host_bodypart = null + + forceMove(get_turf(host)) + + reset_view(null) + + host = null + +/mob/living/simple_mob/animal/sif/leech/verb/inject_victim() + set category = "Abilities" + set name = "Incapacitate Potential Host" + set desc = "Inject an organic host with an incredibly painful mixture of chemicals." + + if(docile) + to_chat(src, span("alium","We are too tired to do this...")) + return + + var/mob/living/carbon/M + if(src.client) + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + if(!choices.len) + to_chat(src, span("warning","There are no viable hosts within range...")) + return + + M = input(src,"Who do we wish to inject?") in null|choices + + if(!M || stat) + return + + poison_inject(usr, M) + +/mob/living/simple_mob/animal/sif/leech/proc/poison_inject(var/mob/living/user, var/mob/living/carbon/L) + if(!L || !Adjacent(L) || stat) + return + + var/mob/living/carbon/human/H = L + + if(!istype(H) || H.isSynthetic()) + to_chat(user, span("warning","You cannot inject this target...")) + + var/obj/item/organ/external/E = H.organs_by_name[infest_target] + if(!E || E.is_stump() || E.robotic >= ORGAN_ROBOT) + to_chat(src,"\The [H] does not have an infestable [infest_target]!") + + var/list/covering_clothing = E.get_covering_clothing() + for(var/obj/item/clothing/C in covering_clothing) + if(C.armor["melee"] >= 40 + attack_armor_pen) + to_chat(user, span("notice","You cannot get through that host's protective gear.")) + return + + H.add_modifier(/datum/modifier/poisoned/paralysis, 15 SECONDS) + +/mob/living/simple_mob/animal/sif/leech/verb/medicate_host() + set category = "Abilities" + set name = "Produce Chemicals (50)" + set desc = "Inject your host with possibly beneficial chemicals, to keep the blood flowing." + + if(docile) + to_chat(src, span("alium","We are too tired to do this...")) + return + + if(!host || chemicals <= 50) + to_chat(usr, span("alien","We cannot produce any chemicals right now.")) + return + + if(host) + var/chem = input("Select a chemical to produce.", "Chemicals") as null|anything in produceable_chemicals + inject_meds(chem) + +/mob/living/simple_mob/animal/sif/leech/proc/inject_meds(var/chem) + if(host) + chemicals = max(1, chemicals - 50) + host.reagents.add_reagent(chem, 5) + to_chat(src, span("alien","We injected \the [host] with five units of [chem].")) + +/mob/living/simple_mob/animal/sif/leech/verb/feed_on_organ() + set category = "Abilities" + set name = "Feed on Organ" + set desc = "Extend probosci to feed on a piece of your host's organs." + + if(docile) + to_chat(src, span("alium","We are too tired to do this...")) + return + + if(host && world.time >= last_feeding + feeding_delay) + var/list/host_internal_organs = host.internal_organs + + for(var/obj/item/organ/internal/O in host_internal_organs) // Remove organs with maximum damage. + if(O.damage >= O.max_damage) + host_internal_organs -= O + + var/target + if(client) + target = input("Select an organ to feed on.", "Organs") as null|anything in host_internal_organs + if(!target) + to_chat(src, span("alien","We decide not to feed.")) + return + + if(!target) + target = pick(host_internal_organs) + + if(target) + bite_organ(target) + + else + to_chat(src, span("warning","We cannot feed now.")) + +/mob/living/simple_mob/animal/sif/leech/proc/bite_organ(var/obj/item/organ/internal/O) + last_feeding = world.time + + if(O) + to_chat(src, span("alien","We feed on [O].")) + O.take_damage(2,silent=prob(10)) + chemicals = min(max_chemicals, chemicals + 60) + host.add_modifier(/datum/modifier/grievous_wounds, 60 SECONDS) + adjustBruteLoss(rand(-10,-60)) + adjustFireLoss(rand(-10,-60)) + +/datum/ai_holder/simple_mob/intentional/leech + hostile = TRUE + retaliate = TRUE + vision_range = 3 + mauling = TRUE + returns_home = TRUE + can_flee = TRUE + home_low_priority = TRUE // If we've got a target, we're going for them. + max_home_distance = 1 // Low to ensure the creature doesn't leave the water unless it has a host. + +/datum/ai_holder/simple_mob/intentional/leech/handle_special_strategical() + var/mob/living/simple_mob/animal/sif/leech/SL = holder + if(!SL.host && !istype(get_turf(SL), /turf/simulated/floor/water)) + var/list/nearby_water = list() + for(var/turf/simulated/floor/water/W in view(holder, 10)) + nearby_water |= W + if(nearby_water && nearby_water.len) + var/turf/T = pick(nearby_water) + if(T && can_attack(T)) + home_turf = T + +/datum/ai_holder/simple_mob/intentional/leech/special_flee_check() + var/mob/living/simple_mob/animal/sif/leech/SL = holder + + if(!SL.host && !istype(get_turf(SL), /turf/simulated/floor/water)) + return TRUE + +/datum/ai_holder/simple_mob/intentional/leech/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(ishuman(L) && !L.isSynthetic()) + if(L.incapacitated() || (L.stat && L.stat != DEAD) || L.resting || L.paralysis) + holder.a_intent = I_GRAB // Infesting time. + else + holder.a_intent = I_DISARM // They're standing up! Try to drop or stun them. + else + holder.a_intent = I_HURT // Otherwise, bite. + + else if(istype(A, /obj/item)) + var/obj/item/I = A + if(istype(I, /obj/item/weapon/reagent_containers/food/snacks)) + holder.a_intent = I_HURT + else + holder.a_intent = I_HURT diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm new file mode 100644 index 0000000000..b2e913bfb2 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm @@ -0,0 +1,355 @@ +// Sakimm are small scavengers with an adoration for shiny things. They won't attack you for them, but you will be their friend holding something like a coin. + +/datum/category_item/catalogue/fauna/sakimm + name = "Sivian Fauna - Sakimm" + desc = "Classification: S Procyon cogitae \ +

      \ + Small, social omnivores known to collect objects within their dens. \ + The Sakimm form colonies that have been known to grow up to a hundred individuals. Primarily carnivorous hunters, \ + they often supplement their diets with nuts, roots, and other fruits. \ + Individuals are known to steal food and reflective objects from unsuspecting Sivian residents. \ + It is advised to keep any valuable items within dull wraps when venturing near the den of a Sakimm." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/sif/sakimm + name = "sakimm" + desc = "What appears to be an oversized rodent with hands." + tt_desc = "S Procyon cogitae" + catalogue_data = list(/datum/category_item/catalogue/fauna/sakimm) + + faction = "sakimm" + + icon_state = "raccoon" + icon_living = "raccoon" + icon_dead = "raccoon_dead" + icon_rest = "raccoon_dead" + icon = 'icons/mob/animal.dmi' + + maxHealth = 50 + health = 50 + has_hands = TRUE + humanoid_hands = TRUE + + pass_flags = PASSTABLE + + universal_understand = 1 + + movement_cooldown = 0 + + melee_damage_lower = 5 + melee_damage_upper = 15 + base_attack_cooldown = 1 SECOND + attacktext = list("nipped", "bit", "cut", "clawed") + + armor = list( + "melee" = 15, + "bullet" = 5, + "laser" = 5, + "energy" = 0, + "bomb" = 10, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 2, + "bullet" = 2, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + say_list_type = /datum/say_list/sakimm + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative/sakimm + + var/obj/item/clothing/head/hat = null // The hat the Sakimm may be wearing. + var/list/friend_loot_list = list(/obj/item/weapon/coin) // What will make this animal non-hostile if held? + var/randomize_size = TRUE + +/mob/living/simple_mob/animal/sif/sakimm/verb/remove_hat() + set name = "Remove Hat" + set desc = "Remove the animal's hat. You monster." + set category = "Abilities" + set src in view(1) + + drop_hat(usr) + +/mob/living/simple_mob/animal/sif/sakimm/proc/drop_hat(var/mob/user) + if(hat) + hat.forceMove(get_turf(user)) + hat = null + update_icon() + if(user == src) + to_chat(user, "You removed your hat.") + return + to_chat(user, "You removed \the [src]'s hat. You monster.") + else + if(user == src) + to_chat(user, "You are not wearing a hat!") + return + to_chat(user, "\The [src] is not wearing a hat!") + +/mob/living/simple_mob/animal/sif/sakimm/verb/give_hat() + set name = "Give Hat" + set desc = "Give the animal a hat. You hero." + set category = "Abilities" + set src in view(1) + + take_hat(usr) + +/mob/living/simple_mob/animal/sif/sakimm/proc/take_hat(var/mob/user) + if(hat) + if(user == src) + to_chat(user, "You already have a hat!") + return + to_chat(user, "\The [src] already has a hat!") + else + if(user == src) + if(istype(get_active_hand(), /obj/item/clothing/head)) + hat = get_active_hand() + drop_from_inventory(hat, src) + hat.forceMove(src) + to_chat(user, "You put on the hat.") + update_icon() + return + else if(ishuman(user)) + var/mob/living/carbon/human/H = user + + if(istype(H.get_active_hand(), /obj/item/clothing/head) && !get_active_hand()) + var/obj/item/clothing/head/newhat = H.get_active_hand() + H.drop_from_inventory(newhat, get_turf(src)) + if(!stat) + intent = I_HELP + newhat.attack_hand(src) + else if(src.get_active_hand()) + to_chat(user, "\The [src] seems busy with \the [get_active_hand()] already!") + + else + to_chat(user, "You aren't holding a hat...") + +/datum/say_list/sakimm + speak = list("Shurr.", "|R|rr?", "Hss.") + emote_see = list("sniffs","looks around", "rubs its hands") + emote_hear = list("chitters", "clicks") + +/mob/living/simple_mob/animal/sif/sakimm/Destroy() + if(hat) + drop_hat(src) + ..() + +/mob/living/simple_mob/animal/sif/sakimm/update_icon() + overlays.Cut() + ..() + if(hat) + var/hat_state = hat.item_state ? hat.item_state : hat.icon_state + var/image/I = image('icons/mob/head.dmi', src, hat_state) + I.pixel_y = -15 // Sakimm are tiny! + I.appearance_flags = RESET_COLOR + add_overlay(I) + +/mob/living/simple_mob/animal/sif/sakimm/Initialize() + . = ..() + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + if(randomize_size) + adjust_scale(rand(8, 11) / 10) + +/mob/living/simple_mob/animal/sif/sakimm/IIsAlly(mob/living/L) + . = ..() + + var/mob/living/carbon/human/H = L + if(!istype(H)) + return . + + if(!.) + var/has_loot = FALSE + var/obj/item/I = H.get_active_hand() + if(I) + for(var/item_type in friend_loot_list) + if(istype(I, item_type)) + has_loot = TRUE + break + return has_loot + +/datum/ai_holder/simple_mob/retaliate/cooperative/sakimm/handle_special_strategical() // Just needs to take hats. + var/mob/living/simple_mob/animal/sif/sakimm/S = holder + + if(holder.get_active_hand() && istype(holder.get_active_hand(), /obj/item/clothing/head) && !S.hat) + var/obj/item/I = holder.get_active_hand() + S.take_hat(S) + holder.visible_message("\The [holder] wears \the [I]") + +/mob/living/simple_mob/animal/sif/sakimm/intelligent + desc = "What appears to be an oversized rodent with hands. This one has a curious look in its eyes." + ai_holder_type = /datum/ai_holder/simple_mob/intentional/sakimm + randomize_size = FALSE // Most likely to have a hat. + +/datum/ai_holder/simple_mob/intentional/sakimm + hostile = FALSE + retaliate = TRUE + vision_range = 10 + can_flee = TRUE + flee_when_dying = TRUE + + var/greed = 0 // The probability we will try to steal something. Increases over time if we are not holding something, or wearing a hat. + var/list/steal_loot_list = list(/obj/item/weapon/coin, /obj/item/weapon/gun, /obj/item/weapon/fossil, /obj/item/stack/material, /obj/item/weapon/material, /obj/item/weapon/reagent_containers/food/snacks, /obj/item/clothing/head, /obj/item/weapon/reagent_containers/glass, /obj/item/device/flashlight, /obj/item/stack/medical, /obj/item/seeds, /obj/item/weapon/spacecash) + var/hoard_items = TRUE + var/hoard_distance = 1 // How far an item can be from the Sakimm's home turf to be counted inside its 'hoard'. + var/original_home_distance = null + var/search_delay = 2 SECONDS // How often can we look for item targets? + var/last_search = 0 + +/datum/ai_holder/simple_mob/intentional/sakimm/New() + ..() + original_home_distance = max_home_distance + +/datum/ai_holder/simple_mob/intentional/sakimm/post_melee_attack(atom/A) + if(istype(A, /obj/item) && !holder.get_active_hand() && holder.Adjacent(A)) + var/obj/item/I = A + I.attack_hand(holder) + lose_target() + if(istype(A,/mob/living) && holder.Adjacent(A)) // Not the dumbest tool in the shed. If we're fighting, we're gonna dance around them. + holder.IMove(get_step(holder, pick(alldirs))) + holder.face_atom(A) + request_help() // And we're going to call friends, too. + +/datum/ai_holder/simple_mob/intentional/sakimm/list_targets() + . = hearers(vision_range, holder) - holder + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha)) + + for(var/HM in typecache_filter_list(range(vision_range, holder), hostile_machines)) + if(can_see(holder, HM, vision_range)) + . += HM + + if(holder.get_active_hand()) // We don't want item targets if we have an item! + return . + + if(world.time <= last_search + search_delay) // Don't spam searching for item targets, since they can be in areas with a -lot- of items. + return . + + for(var/obj/item/I in view(holder, vision_range)) + last_search = world.time + if(!hoard_items || get_dist(I, home_turf) <= 1) + continue + for(var/itemtype in steal_loot_list) + if(istype(I, itemtype)) + if(!I.anchored) + . += I + break + + . -= holder.contents + +/datum/ai_holder/simple_mob/intentional/sakimm/find_target(var/list/possible_targets, var/has_targets_list = FALSE) + var/can_pick_mobs = TRUE + + if(!hostile) + can_pick_mobs = FALSE + + . = list() + if(!has_targets_list) + possible_targets = list_targets() + + for(var/possible_target in possible_targets) + var/atom/A = possible_target + if(found(A)) + . = list(A) + break + if(istype(A, /mob/living) && !can_pick_mobs) + continue + if(can_attack(A)) // Can we attack it? + . += A + continue + + for(var/obj/item/I in .) + last_search = world.time + if(!hoard_items || get_dist(I, home_turf) <= 1) + . -= I + + var/new_target = pick_target(.) + give_target(new_target) + return new_target + +/datum/ai_holder/simple_mob/intentional/sakimm/pre_melee_attack(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(holder.get_active_hand()) // Are we holding something? If so, drop it, we have a new target to kill, and we shouldn't use their weapons. + holder.drop_from_inventory(holder.get_active_hand(), get_turf(holder)) + + if(ishuman(L)) + if(L.incapacitated(INCAPACITATION_DISABLED)) // Is our target on the ground? If so, let's scratch! + if(prob(30)) // Unless we decide to cut and run, and take their stuff while we're at it. + var/turf/T = get_turf(L) + var/obj/item/IT = locate() in T.contents + if(IT) + lose_target() + give_target(IT) + return + holder.a_intent = I_HURT + else + holder.a_intent = I_DISARM // Otherwise, try to disarm them! + else + holder.a_intent = I_HURT // We can't disarm you, so we're going to hurt you. + + else if(istype(A, /obj/item)) + var/obj/item/I = A + if(istype(I, /obj/item/weapon/reagent_containers/food/snacks)) // If we can't pick it up, or it's edible, go to harm. + holder.a_intent = I_HURT + else + holder.a_intent = I_HELP + + else + holder.a_intent = I_HURT + +/datum/ai_holder/simple_mob/intentional/sakimm/should_go_home() + if((!returns_home && !holder.get_active_hand()) || !home_turf) // If we have an item, we want to go home. + return FALSE + if(get_dist(holder, home_turf) > max_home_distance) + if(!home_low_priority) + return TRUE + else if(!leader && !target) + return TRUE + return FALSE + +/datum/ai_holder/simple_mob/intentional/sakimm/handle_special_tactic() + var/mob/living/simple_mob/animal/sif/sakimm/S = holder + if(S.hat) + hoard_items = FALSE + else + hoard_items = TRUE + +/datum/ai_holder/simple_mob/intentional/sakimm/handle_special_strategical() + var/mob/living/simple_mob/animal/sif/sakimm/S = holder + var/carrying_item = FALSE + + if(holder.get_active_hand()) // Do we have loot? + if(istype(holder) && istype(holder.get_active_hand(), /obj/item/clothing/head) && !S.hat) + var/obj/item/I = holder.get_active_hand() + S.take_hat(S) + holder.visible_message("\The [holder] wears \the [I]") + carrying_item = TRUE + + if(istype(holder) && S.hat) // Do we have a hat? Hats are loot. + carrying_item = TRUE + + if(!carrying_item) // Not carrying or wearing anything? We want to carry something more. + greed++ + greed = min(95, greed) + else + greed = 0 + if(!target && prob(5 + greed) && !holder.get_active_hand()) + find_target() + if(holder.get_active_hand() && hoard_items) + lose_target() + max_home_distance = 1 + if(get_dist(holder, home_turf) <= max_home_distance) + holder.drop_from_inventory(holder.get_active_hand(), get_turf(holder)) + if(!holder.get_active_hand()) + max_home_distance = original_home_distance + +/datum/ai_holder/simple_mob/intentional/sakimm/special_flee_check() + return holder.get_active_hand() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm index 881615138e..a6948a3e00 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm @@ -67,7 +67,8 @@ name = "big shantak" desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ This one seems bigger than the others, and has a commanding presence." - icon_scale = 1.5 + icon_scale_x = 1.5 + icon_scale_y = 1.5 maxHealth = 125 player_msg = "You have the ability to command other shantaks to follow you." @@ -94,5 +95,12 @@ // These ones only retaliate. Used for a PoI. -/mob/living/simple_mob/animal/sif/shantak/retaliate - ai_holder_type = /datum/ai_holder/simple_mob/retaliate \ No newline at end of file +/mob/living/simple_mob/animal/sif/shantak/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +//Vorestation Addition +/mob/living/simple_mob/animal/sif/shantak/scruffy + name = "Scruffy" + ai_holder_type = /datum/ai_holder/simple_mob/passive + makes_dirt = 0 + faction = "neutral" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp_vr.dm new file mode 100644 index 0000000000..c678c43f2d --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp_vr.dm @@ -0,0 +1,2 @@ +/mob/living/simple_mob/animal/space/carp + base_attack_cooldown = 15 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm new file mode 100644 index 0000000000..203bd79109 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm @@ -0,0 +1,7 @@ +/datum/category_item/catalogue/fauna/geese + name = "Planetary Fauna - Geese" + desc = "A goose. HONK. Not much to catalogue, they're exactly the same as their earth counterparts." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/space/goose/virgo3b + faction = "virgo3b" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/mimic_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/mimic_vr.dm deleted file mode 100644 index c75560534f..0000000000 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/mimic_vr.dm +++ /dev/null @@ -1,244 +0,0 @@ -// -// Abstract Class -// - -/mob/living/simple_mob/animal/space/mimic - name = "crate" - desc = "A rectangular steel crate." - - icon_state = "crate" - icon_living = "crate" - icon = 'icons/obj/storage.dmi' - - faction = "mimic" - - maxHealth = 250 - health = 250 - //speed = 4 no idea what this is, conflicts with new AI update. - movement_cooldown = 10 //slow crate. - - response_help = "touches" - response_disarm = "pushes" - response_harm = "hits" - - harm_intent_damage = 5 - melee_damage_lower = 4 - melee_damage_upper = 6 - attacktext = list("attacked") - attack_sound = 'sound/weapons/bite.ogg' - - 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 - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat - - say_list_type = /datum/say_list/mimic - ai_holder_type = /datum/ai_holder/mimic - - var/knockdown_chance = 15 //Stubbing your toe on furniture hurts. - - showvoreprefs = 0 //VOREStation Edit - Hides mechanical vore prefs for mimics. You can't see their gaping maws when they're just sitting idle. - -/datum/say_list/mimic - say_got_target = list("growls") - -/datum/ai_holder/mimic - wander = FALSE - hostile = TRUE - threaten = TRUE - threaten_timeout = 5 SECONDS - threaten_delay = 1 SECONDS //not a threat, more of a delay. - -/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(prob(knockdown_chance)) - L.Weaken(3) - L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) - -/* should be covered by my "growls" say thing, but keeping it just in case. -/mob/living/simple_mob/animal/space/mimic/set_target() - . = ..() - if(.) - audible_emote("growls at [.]") -*/ -/mob/living/simple_mob/animal/space/mimic/death() - ..() - qdel(src) - -/mob/living/simple_mob/animal/space/mimic/will_show_tooltip() - return FALSE - -/mob/living/simple_mob/animal/space/mimic/death() - var/obj/structure/closet/crate/C = new(get_turf(src)) - // Put loot in crate - for(var/obj/O in src) - if(isbelly(O)) //VOREStation edit - continue - O.forceMove(C) - ..() - -/mob/living/simple_mob/animal/space/mimic/Initialize() - . = ..() - for(var/obj/item/I in loc) - I.forceMove(src) -/* I honestly have no idea what's happening down there so I'm just taking the essentials and yeeting. -// -// Crate Mimic -// - -// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. -/mob/living/simple_mob/animal/space/mimic/crate - - attacktext = list("bitten") - -// stop_automated_movement = 1 we don't need these -// wander = 0 - var/attempt_open = 0 - -// Pickup loot -/mob/living/simple_mob/animal/space/mimic/crate/Initialize() - . = ..() - for(var/obj/item/I in loc) - I.forceMove(src) -/* I can't find an equivilant to this, don't know why we really have it even so... -/mob/living/simple_mob/animal/space/mimic/crate/DestroySurroundings() - ..() - if(prob(90)) - icon_state = "[initial(icon_state)]open" - else - icon_state = initial(icon_state) -*/ -/mob/living/simple_mob/animal/space/mimic/crate/ListTargets() - if(attempt_open) - return ..() - else - return ..(1) - -/mob/living/simple_mob/animal/space/mimic/crate/set_target() - . = ..() - if(.) - trigger() - -/mob/living/simple_mob/animal/space/mimic/crate/PunchTarget() - . = ..() - if(.) - icon_state = initial(icon_state) - -/mob/living/simple_mob/animal/space/mimic/crate/proc/trigger() - if(!attempt_open) - visible_message("[src] starts to move!") - attempt_open = 1 - -/mob/living/simple_mob/animal/space/mimic/crate/adjustBruteLoss(var/damage) - trigger() - ..(damage) - -/mob/living/simple_mob/animal/space/mimic/crate/LoseTarget() - ..() - icon_state = initial(icon_state) - -/mob/living/simple_mob/animal/space/mimic/crate/LostTarget() - ..() - icon_state = initial(icon_state) - -/mob/living/simple_mob/animal/space/mimic/crate/death() - var/obj/structure/closet/crate/C = new(get_turf(src)) - // Put loot in crate - for(var/obj/O in src) - if(isbelly(O)) //VOREStation edit - continue - O.forceMove(C) - ..() - -/mob/living/simple_mob/animal/space/mimic/crate/PunchTarget() - . =..() - var/mob/living/L = . - if(istype(L)) - if(prob(15)) - L.Weaken(2) - L.visible_message("\the [src] knocks down \the [L]!") - -// -// Copy Mimic -// - -var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window, /obj/item/projectile/animate) - -/mob/living/simple_mob/animal/space/mimic/copy - - health = 100 - maxHealth = 100 - var/mob/living/creator = null // the creator - var/destroy_objects = 0 - var/knockdown_people = 0 - -/mob/living/simple_mob/animal/space/mimic/copy/New(loc, var/obj/copy, var/mob/living/creator) - ..(loc) - CopyObject(copy, creator) - -/mob/living/simple_mob/animal/space/mimic/copy/death() - - for(var/atom/movable/M in src) - if(isbelly(M)) //VOREStation edit - continue - M.forceMove(get_turf(src)) - ..() - -/mob/living/simple_mob/animal/space/mimic/copy/ListTargets() - // Return a list of targets that isn't the creator - . = ..() - return . - creator - -/mob/living/simple_mob/animal/space/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/creator) - - if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, protected_objects)) - - O.forceMove(src) - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - icon_living = icon_state - - if(istype(O, /obj/structure)) - health = (anchored * 50) + 50 - destroy_objects = 1 - if(O.density && O.anchored) - knockdown_people = 1 - melee_damage_lower *= 2 - melee_damage_upper *= 2 - else if(istype(O, /obj/item)) - var/obj/item/I = O - health = 15 * I.w_class - melee_damage_lower = 2 + I.force - melee_damage_upper = 2 + I.force - move_to_delay = 2 * I.w_class - - maxHealth = health - if(creator) - src.creator = creator - faction = "\ref[creator]" // very unique - return 1 - return - -/mob/living/simple_mob/animal/space/mimic/copy/DestroySurroundings() - if(destroy_objects) - ..() - -/mob/living/simple_mob/animal/space/mimic/copy/PunchTarget() - . =..() - if(knockdown_people) - var/mob/living/L = . - if(istype(L)) - if(prob(15)) - L.Weaken(1) - L.visible_message("\the [src] knocks down \the [L]!") -*/ \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/space_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/space_vr.dm new file mode 100644 index 0000000000..082f6342d0 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/space_vr.dm @@ -0,0 +1,3 @@ +// Fix for Virgo 2's Surface +/mob/living/simple_mob/animal/space + maxbodytemp = 700 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm new file mode 100644 index 0000000000..9fbb2926bb --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm @@ -0,0 +1,428 @@ +/mob/living/simple_mob/animal/space/space_worm + name = "space worm segment" + desc = "A part of a space worm." + icon = 'icons/mob/worm.dmi' + icon_state = "spaceworm" + icon_living = "spaceworm" + icon_dead = "spacewormdead" + + tt_desc = "U Tyranochaetus imperator" + + anchored = TRUE // Theoretically, you shouldn't be able to move this without moving the head. + + maxHealth = 200 + health = 200 + movement_cooldown = 0 + + faction = "worm" + + status_flags = 0 + universal_speak = 0 + universal_understand = 1 + animate_movement = SYNC_STEPS + + response_help = "touches" + response_disarm = "flails at" + response_harm = "punches the" + + harm_intent_damage = 2 + + attacktext = list("slammed") + + ai_holder_type = /datum/ai_holder/simple_mob/inert + + mob_class = MOB_CLASS_ABERRATION // It's a monster. + + meat_amount = 2 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/worm + + var/mob/living/simple_mob/animal/space/space_worm/previous //next/previous segments, correspondingly + var/mob/living/simple_mob/animal/space/space_worm/next //head is the nextest segment + + var/severed = FALSE // Is this a severed segment? + + var/severed_head_type = /mob/living/simple_mob/animal/space/space_worm/head/severed // What type of head do we spawn when detaching? + var/segment_type = /mob/living/simple_mob/animal/space/space_worm // What type of segment do our heads make? + + var/stomachProcessProbability = 50 + var/digestionProbability = 20 + var/flatPlasmaValue = 5 //flat Phoron amount given for non-items + + var/atom/currentlyEating // Worm's current Maw target. + + var/z_transitioning = FALSE // Are we currently moving between Z-levels, or doing something that might mean we can't rely on distance checking for segments? + var/sever_chunks = FALSE // Do we fall apart when dying? + + var/time_maw_opened = 0 + var/maw_cooldown = 30 SECONDS + var/open_maw = FALSE // Are we trying to eat things? + +/mob/living/simple_mob/animal/space/space_worm/head + name = "space worm" + icon_state = "spacewormhead" + icon_living = "spacewormhead" + + anchored = FALSE // You can pull the head to pull the body. + + maxHealth = 300 + health = 300 + + hovering = TRUE + + melee_damage_lower = 10 + melee_damage_upper = 25 + attack_sharp = TRUE + attack_edge = TRUE + attack_armor_pen = 30 + attacktext = list("bitten", "gored", "gouged", "chomped", "slammed") + + animate_movement = SLIDE_STEPS + + var/segment_count = 6 + +/mob/living/simple_mob/animal/space/space_worm/head/severed + segment_count = 0 + severed = TRUE + +/mob/living/simple_mob/animal/space/space_worm/head/short + segment_count = 3 + +/mob/living/simple_mob/animal/space/space_worm/head/long + segment_count = 10 + +/mob/living/simple_mob/animal/space/space_worm/head/handle_special() + ..() + update_body_faction() + +/mob/living/simple_mob/animal/space/space_worm/head/update_icon() + ..() + if(!open_maw && !stat) + icon_state = "[icon_living][previous ? 1 : 0]_hunt" + else + icon_state = "[icon_living][previous ? 1 : 0]" + + if(previous) + set_dir(get_dir(previous,src)) + + if(stat) + icon_state = "[icon_state]_dead" + +/mob/living/simple_mob/animal/space/space_worm/head/Initialize() + ..() + + var/mob/living/simple_mob/animal/space/space_worm/current = src + + if(segment_count && !severed) + for(var/i = 1 to segment_count) + var/mob/living/simple_mob/animal/space/space_worm/newSegment = new segment_type(loc) + current.Attach(newSegment) + current = newSegment + current.faction = faction + +/mob/living/simple_mob/animal/space/space_worm/head/verb/toggle_devour() + set name = "Toggle Feeding" + set desc = "Extends your teeth for 30 seconds so that you can chew through mobs and structures alike." + set category = "Abilities" + + if(world.time < time_maw_opened + maw_cooldown) + if(open_maw) + to_chat(src, "You retract your teeth.") + time_maw_opened -= maw_cooldown / 2 // Recovers half cooldown if you end it early manually. + else + to_chat(src, "You are too tired to do this..") + set_maw(FALSE) + else + set_maw(!open_maw) + +/mob/living/simple_mob/animal/space/space_worm/proc/set_maw(var/state = FALSE) + open_maw = state + if(open_maw) + time_maw_opened = world.time + movement_cooldown = initial(movement_cooldown) + 1.5 + else + movement_cooldown = initial(movement_cooldown) + update_icon() + +/mob/living/simple_mob/animal/space/space_worm/death() + ..() + + DumpStomach() + + if(previous) + previous.death() + +/mob/living/simple_mob/animal/space/space_worm/handle_special() // Processed in life. Nicer to have it modular incase something in Life change(d)(s) + ..() + + if(world.time > time_maw_opened + maw_cooldown) // Auto-stop eating. + if(open_maw) + to_chat(src, "Your jaws cannot remain open..") + set_maw(FALSE) + + if(next && !(next in view(src,1)) && !z_transitioning) + Detach(1) + + if(stat == DEAD && sever_chunks) // Dead chunks fall off and die immediately if we sever_chunks + if(previous) + previous.Detach(1) + if(next) + Detach(1) + + if(prob(stomachProcessProbability)) + ProcessStomach() + + update_icon() + + return + +/mob/living/simple_mob/animal/space/space_worm/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /mob/living/simple_mob/animal/space/space_worm/head)) + var/mob/living/simple_mob/animal/space/space_worm/head/H = mover + if(H.previous == src) + return FALSE + + if(istype(mover, /mob/living/simple_mob/animal/space/space_worm)) // Worms don't run over worms. That's weird. And also really annoying. + return TRUE + else if(src.stat == DEAD && !istype(mover, /obj/item/projectile)) // Projectiles need to do their normal checks. + return TRUE + return ..() + +/mob/living/simple_mob/animal/space/space_worm/Destroy() // If a chunk is destroyed, kill the back half. + DumpStomach() + if(previous) + previous.Detach(1) + if(next) + next.previous = null + next = null + ..() + +/mob/living/simple_mob/animal/space/space_worm/Move() + var/attachementNextPosition = loc + . = ..() + if(.) + if(previous) + if(previous.z != z) + previous.z_transitioning = TRUE + else + previous.z_transitioning = FALSE + previous.forceMove(attachementNextPosition) // None of this 'ripped in half by an airlock' business. + update_icon() + +/mob/living/simple_mob/animal/space/space_worm/forceMove() + var/attachementNextPosition = loc + . = ..() + if(.) + if(previous) + if(previous.z != z) + previous.z_transitioning = TRUE + else + previous.z_transitioning = FALSE + previous.forceMove(attachementNextPosition) // None of this 'ripped in half by an airlock' business. x 2 + update_icon() + +/mob/living/simple_mob/animal/space/space_worm/head/Bump(atom/obstacle) + if(open_maw && !stat && obstacle != previous) + spawn(1) + if(currentlyEating != obstacle) + currentlyEating = obstacle + + set_AI_busy(TRUE) + if(AttemptToEat(obstacle)) + currentlyEating = null + set_AI_busy(FALSE) + else + currentlyEating = null + . = ..(obstacle) + +/mob/living/simple_mob/animal/space/space_worm/update_icon() + if(previous) //midsection + icon_state = "spaceworm[get_dir(src,previous) | get_dir(src,next)]" + if(stat) + icon_state = "[icon_state]_dead" + + else //tail + icon_state = "spacewormtail" + if(stat) + icon_state = "[icon_state]_dead" + set_dir(get_dir(src,next)) + + if(next) + color = next.color + + return + +/mob/living/simple_mob/animal/space/space_worm/proc/AttemptToEat(var/atom/target) + if(istype(target,/turf/simulated/wall)) + var/turf/simulated/wall/W = target + if((!W.reinf_material && do_after(src, 5 SECONDS)) || do_after(src, 10 SECONDS)) // 10 seconds for an R-wall, 5 seconds for a normal one. + if(target) + W.dismantle_wall() + return 1 + else if(istype(target,/atom/movable)) + if(istype(target,/mob) || do_after(src, 5)) // 5 ticks to eat stuff like tables. + var/atom/movable/objectOrMob = target + if(istype(objectOrMob, /obj/machinery/door)) // Doors and airlocks take time based on their durability and our damageo. + var/obj/machinery/door/D = objectOrMob + var/total_hits = max(2, round(D.maxhealth / (2 * melee_damage_upper))) + + for(var/I = 1 to total_hits) + + if(!D) + objectOrMob = null + break + + if(do_after(src, 5)) + D.visible_message("Something crashes against \the [D]!") + D.take_damage(2 * melee_damage_upper) + else + objectOrMob = null + break + + if(D && (D.stat & BROKEN|NOPOWER)) + D.open(TRUE) + break + + if(istype(objectOrMob, /obj/effect/energy_field)) + var/obj/effect/energy_field/EF = objectOrMob + objectOrMob = null // No eating shields. + if(EF.opacity) + EF.visible_message("Something begins forcing itself through \the [EF]!") + else + EF.visible_message("\The [src] begins forcing itself through \the [EF]!") + if(do_after(src, EF.strength * 5)) + EF.adjust_strength(rand(-8, -10)) + EF.visible_message("\The [src] crashes through \the [EF]!") + else + EF.visible_message("\The [EF] reverberates as it returns to normal.") + + if(objectOrMob) + objectOrMob.update_nearby_tiles(need_rebuild=1) + objectOrMob.forceMove(src) + return 1 + + return 0 + +/mob/living/simple_mob/animal/space/space_worm/proc/Attach(var/mob/living/simple_mob/animal/space/space_worm/attachement) + if(!attachement) + return + + previous = attachement + attachement.next = src + + return + +/mob/living/simple_mob/animal/space/space_worm/proc/Detach(die = 0) + var/mob/living/simple_mob/animal/space/space_worm/head/newHead = new severed_head_type(loc,0) + var/mob/living/simple_mob/animal/space/space_worm/newHeadPrevious = previous + + previous = null //so that no extra heads are spawned + + newHead.Attach(newHeadPrevious) + + if(die) + newHead.death() + + qdel(src) + +/mob/living/simple_mob/animal/space/space_worm/proc/ProcessStomach() + for(var/atom/movable/stomachContent in contents) + if(stomach_special(stomachContent)) + continue + if(prob(digestionProbability)) + if(stomach_special_digest(stomachContent)) + continue + if(istype(stomachContent,/obj/item/stack)) //converts to plasma, keeping the stack value + if(!istype(stomachContent,/obj/item/stack/material/phoron)) + var/obj/item/stack/oldStack = stomachContent + new /obj/item/stack/material/phoron(src, oldStack.get_amount()) + qdel(oldStack) + continue + else if(istype(stomachContent,/obj/item)) //converts to plasma, keeping the w_class + var/obj/item/oldItem = stomachContent + new /obj/item/stack/material/phoron(src, oldItem.w_class) + qdel(oldItem) + continue + else + new /obj/item/stack/material/phoron(src, flatPlasmaValue) //just flat amount + if(!isliving(stomachContent)) + qdel(stomachContent) + else + var/mob/living/L = stomachContent + if(iscarbon(L)) + var/mob/living/carbon/C = L + var/damage_cycles = rand(3, 5) + for(var/I = 0, I < damage_cycles, I++) + C.apply_damage(damage = rand(10,20), damagetype = BIOACID, def_zone = pick(BP_ALL)) + else + L.apply_damage(damage = rand(10,60), damagetype = BIOACID) + continue + + DumpStomach() + + return + +/mob/living/simple_mob/animal/space/space_worm/proc/DumpStomach() + if(previous && previous.stat != DEAD) + for(var/atom/movable/stomachContent in contents) //transfer it along the digestive tract + stomachContent.forceMove(previous) + else + for(var/atom/movable/stomachContent in contents) // Or dump it out. + stomachContent.forceMove(get_turf(src)) + return + +/mob/living/simple_mob/animal/space/space_worm/proc/stomach_special(var/atom/A) // Futureproof. Anything that interacts with contents without relying on digestion probability. Return TRUE if it should skip digest. + return FALSE + +/mob/living/simple_mob/animal/space/space_worm/proc/stomach_special_digest(var/atom/A) // Futureproof. Any special checks that interact with digested atoms. I.E., ore processing. Return TRUE if it should skip future digest checks. + return FALSE + +/mob/living/simple_mob/animal/space/space_worm/proc/update_body_faction() + if(next) // Keep us on the same page, here. + faction = next.faction + if(previous) + previous.update_body_faction() + return 1 + return 0 + +// Worm meat. + +/obj/item/weapon/reagent_containers/food/snacks/meat/worm + name = "meat" + desc = "A chunk of pulsating meat." + icon_state = "wormmeat" + health = 180 + filling_color = "#551A8B" + center_of_mass = list("x"=16, "y"=14) + +/obj/item/weapon/reagent_containers/food/snacks/meat/worm/Initialize() + . = ..() + reagents.add_reagent("protein", 6) + reagents.add_reagent("phoron", 3) + reagents.add_reagent("myelamine", 3) + src.bitesize = 3 + +/obj/item/weapon/reagent_containers/food/snacks/meat/worm/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/material/knife)) + var/to_spawn = pickweight(/obj/random/junk = 30, + /obj/random/trash = 30, + /obj/random/maintenance/clean = 15, + /obj/random/tool = 15, + /obj/random/medical = 3, + /obj/random/bomb_supply = 7, + /obj/random/contraband = 3, + /obj/random/unidentified_medicine/old_medicine = 7, + /obj/item/weapon/strangerock = 3, + /obj/item/weapon/ore/phoron = 7, + /obj/random/handgun = 1, + /obj/random/toolbox = 4, + /obj/random/drinkbottle = 5 + ) + + new to_spawn(get_turf(src)) + + if(prob(20)) + user.visible_message("Something oozes out of \the [src] as it is cut.") + + to_chat(user, "You cut the tissue holding the chunks together.") + + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm new file mode 100644 index 0000000000..62a172c823 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/Eddy + name = "???" + desc = "A dark green, sluglike creature, covered in glowing green ooze, and carrying what look to be eggs on its back." + + icon_state = "Eddy" + icon_living = "Eddy" + icon_dead = "e_head" + icon_rest = "Eddy" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/negative.ogg' + + maxHealth = 175 + health = 175 + + melee_damage_lower = 25 + melee_damage_upper = 35 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("amashes") + friendly = list("nuzzles", "boops", "bumps against", "leans on") + + + say_list_type = /datum/say_list/Eddy + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/Eddy/death() + playsound(src, 'sound/h_sounds/headcrab.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Eddy/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Eddy/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Eddy/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Eddy/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/Eddy + speak = list("Uuurrgh?","Aauuugghh...", "AAARRRGH!") + emote_hear = list("shrieks horrifically", "groans in pain", "cries", "whines") + emote_see = list("blinks its many eyes", "shakes violently in place", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm new file mode 100644 index 0000000000..50b454370f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm @@ -0,0 +1,50 @@ +/mob/living/simple_mob/horror/Master + name = "Dr. Helix" + desc = "A massive pile of grotesque flesh and bulging tumor like growths. Every inch of its skin is undulating in every direction possible, bringing a literal definition to 'Skin Crawling.' Stuck in the middle of this monstrosity is a large AI core with a bloodied, emaciated man sewn into its circuitry." + + icon_state = "Helix" + icon_living = "Helix" + icon_dead = "m_dead" + icon_rest = "Helix" + faction = "horror" + icon = 'icons/mob/horror_show/master.dmi' + icon_gib = "generic_gib" + anchored = 1 + + attack_sound = 'sound/h_sounds/shitty_tim.ogg' + + maxHealth = 400 + health = 400 + + melee_damage_lower = 5 + melee_damage_upper = 8 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("smushes") + friendly = list("nuzzles", "boops", "bumps against", "leans on") + + + ai_holder_type = null + +/mob/living/simple_mob/horror/Master/death() + playsound(src, 'sound/h_sounds/imbeciles.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Master/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Master/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Master/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Master/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm new file mode 100644 index 0000000000..ecc847e8ba --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/Rickey + name = "???" + desc = "What a handsome Man, his mother must think." + + icon_state = "Rickey" + icon_living = "Rickey" + icon_dead = "r_head" + icon_rest = "Rickey" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/wor.ogg' + + maxHealth = 175 + health = 175 + + melee_damage_lower = 25 + melee_damage_upper = 35 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("amashes") + friendly = list("nuzzles", "boops", "bumps against", "leans on") + + + say_list_type = /datum/say_list/Rickey + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/Rickey/death() + playsound(src, 'sound/h_sounds/headcrab.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Rickey/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Rickey/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Rickey/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Rickey/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/Rickey + speak = list("Uuurrgh?","Aauuugghh...", "AAARRRGH!") + emote_hear = list("shrieks horrifically", "groans in pain", "cries", "whines") + emote_see = list("flexes to no one in particular", "shakes violently in place", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm new file mode 100644 index 0000000000..f9e98876b5 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/Smiley + name = "???" + desc = "A giant hand, with a large, smiling head on top." + + icon_state = "Smiley" + icon_living = "Smiley" + icon_dead = "s_head" + icon_rest = "Smiley" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/holla.ogg' + + maxHealth = 175 + health = 175 + + melee_damage_lower = 25 + melee_damage_upper = 35 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("amashes") + friendly = list("nuzzles", "boops", "bumps against", "leans on") + + + say_list_type = /datum/say_list/Smiley + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/Smiley/death() + playsound(src, 'sound/h_sounds/lynx.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Helix/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Helix/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Helix/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Helix/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/Smiley + speak = list("Uuurrgh?","Aauuugghh...", "AAARRRGH!") + emote_hear = list("shrieks horrifically", "groans in pain", "cries", "whines") + emote_see = list("squeezes its fingers together", "shakes violently in place", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm new file mode 100644 index 0000000000..f06f8b8de7 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm @@ -0,0 +1,64 @@ +/mob/living/simple_mob/horror/Steve + name = "???" + desc = "A formless blob of flesh with one, giant, everblinking eye. It has a large machine gun and a watercooler stuck stright into its skin." + + icon_state = "Steve" + icon_living = "Steve" + icon_dead = "sg_head" + icon_rest = "Steve" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/mumble.ogg' + + maxHealth = 175 + health = 175 + + melee_damage_lower = 25 + melee_damage_upper = 35 + grab_resist = 100 + + projectiletype = /obj/item/projectile/bullet/pistol/medium + projectilesound = 'sound/weapons/Gunshot_light.ogg' + + needs_reload = TRUE + base_attack_cooldown = 5 // Two attacks a second or so. + reload_max = 20 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("amashes") + friendly = list("nuzzles", "boops", "bumps against", "leans on") + + + say_list_type = /datum/say_list/Steve + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/Steve/death() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Steve/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Steve/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Steve/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Steve/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/Steve + speak = list("Uuurrgh?","Aauuugghh...", "AAARRRGH!") + emote_hear = list("shrieks horrifically", "groans in pain", "cries", "whines") + emote_see = list("blinks aggressively at", "shakes violently in place", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm new file mode 100644 index 0000000000..3cdeb93410 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/Willy + name = "???" + desc = "It looks like a giant mascot costume made of flesh and fabric. The two bulging eyes aren't comforting to look at either. At least it smells like a burger and fries." + + icon_state = "Willy" + icon_living = "Willy" + icon_dead = "w_head" + icon_rest = "Willy" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/negative.ogg' + + maxHealth = 175 + health = 175 + + melee_damage_lower = 25 + melee_damage_upper = 35 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("amashes") + friendly = list("nuzzles", "boops", "bumps against", "leans on") + + + say_list_type = /datum/say_list/Willy + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/Willy/death() + playsound(src, 'sound/h_sounds/sampler.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Willy/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Willy/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Willy/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Willy/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/Willy + speak = list("Uuurrgh?","Aauuugghh...", "AAARRRGH!") + emote_hear = list("shrieks horrifically", "groans in pain", "cries", "whines") + emote_see = list("headbobs", "shakes violently in place", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm new file mode 100644 index 0000000000..eda784428b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/bradley + name = "Bradley" + desc = "What you see is a ball of seemingly melty flesh, stitched together hastily over large, bulging scars. Four metal legs extend out of its sides, The two in the front are larger than the back; and all of the legs are segmented with a unique steel looking metal. In the middle of this monstrosity is a constantly tremmoring eye. While the eye never blinks, it is dyed faintly yellow, with a vertical, read pupil. It seems like it's crying, a weird, oil like liquid seeping from its socket." + + icon_state = "Bradley" + icon_living = "Bradley" + icon_dead = "b_head" + icon_rest = "Bradley" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/holla.ogg' + + maxHealth = 175 + health = 175 + + melee_damage_lower = 25 + melee_damage_upper = 35 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("mutilate") + friendly = list("nuzzles", "eyeboops", "headbumps against", "leans on") + + + say_list_type = /datum/say_list/bradley + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/bradley/death() + playsound(src, 'sound/h_sounds/mumble.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/bradley/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/bradley/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/bradley/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/bradley/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/bradley + speak = list("Uuurrgh?","Aauuugghh...", "AAARRRGH!") + emote_hear = list("shrieks through its skin", "groans in pain", "creaks", "clanks") + emote_see = list("taps its limbs against the ground", "shakes", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm b/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm new file mode 100644 index 0000000000..b663972372 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm @@ -0,0 +1,27 @@ +/mob/living/simple_mob/horror + tt_desc = "Homo Horrificus" + faction = "horror" + icon = 'icons/mob/horror_show/GHPS.dmi' + icon_gib = "generic_gib" + +/datum/ai_holder/simple_mob/horror + hostile = TRUE // The majority of simplemobs are hostile, gaslamps are nice. + cooperative = FALSE + retaliate = TRUE //so the monster can attack back + returns_home = FALSE + can_flee = FALSE + speak_chance = 3 + wander = TRUE + base_wander_delay = 9 + +/mob/living/simple_mob/horror + 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 = 700 diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm new file mode 100644 index 0000000000..33713fe114 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/Sally + name = "???" + desc = "A mass of tentacles hold up a large head, graced with one of the grandest smiles in the galaxy. It's a shame about the constant oil leaking from its eyes." + + icon_state = "Sally" + icon_living = "Sally" + icon_dead = "ws_head" + icon_rest = "Sally" + faction = "horror" + icon = 'icons/mob/horror_show/widehorror.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/sampler.ogg' + + maxHealth = 200 + health = 200 + + melee_damage_lower = 30 + melee_damage_upper = 40 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("smashes") + friendly = list("nuzzles", "boops", "headbumps against", "leans on") + + + say_list_type = /datum/say_list/Sally + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/Sally/death() + playsound(src, 'sound/h_sounds/lynx.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Sally/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Sally/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Sally/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/Sally/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/Sally + speak = list("Yeeeeee?","Haaah! Gashuuuuuh!", "Gahgahgahgah...") + emote_hear = list("shrieks", "groans in pain", "breathes heavily", "gnashes its teeth") + emote_see = list("wiggles its head", "shakes violently", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm new file mode 100644 index 0000000000..105ff22315 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/BigTim + name = "Shitty Tim" + desc = "A tall figure wearing ripped clothes. Its eyes are placed on the bulb of skin that's folded over the front of its face. He has a gold clock hanging on a gold chain around his neck, and he has a gold and diamond bracelet on his wrist." + + icon_state = "shitty_tim" + icon_living = "shitty_tim" + icon_dead = "tst_head" + icon_rest = "shitty_tim" + faction = "horror" + icon = 'icons/mob/horror_show/tallhorror.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/youknowwhoitis.ogg' + + maxHealth = 250 + health = 250 + + melee_damage_lower = 35 + melee_damage_upper = 45 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("mutilate") + friendly = list("nuzzles", "boops", "headbumps against", "leans on") + + + say_list_type = /datum/say_list/BigTim + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/BigTim/death() + playsound(src, 'sound/h_sounds/shitty_tim.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/BigTim/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/BigTim/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/BigTim/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/BigTim/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/BigTim + speak = list("Wuuuuuhhuuhhhhh?","Urk! Aaaaahaaa!", "Yuhyuhyuhyuh...") + emote_hear = list("shrieks", "groans in pain", "flaps", "gnashes its teeth") + emote_see = list("jiggles its teeth", "shakes violently", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm new file mode 100644 index 0000000000..6985c3ec8c --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm @@ -0,0 +1,57 @@ +/mob/living/simple_mob/horror/TinyTim + name = "???" + desc = "A tall figure wearing ripped clothes. Its eyes are placed on the bulb of skin that's folded over the front of its face." + + icon_state = "timling" + icon_living = "timling" + icon_dead = "tt_head" + icon_rest = "timling" + faction = "horror" + icon = 'icons/mob/horror_show/tallhorror.dmi' + icon_gib = "generic_gib" + + attack_sound = 'sound/h_sounds/youknowwhoitis.ogg' + + maxHealth = 200 + health = 200 + + melee_damage_lower = 30 + melee_damage_upper = 40 + grab_resist = 100 + + response_help = "pets the" + response_disarm = "bops the" + response_harm = "hits the" + attacktext = list("mutilate") + friendly = list("nuzzles", "boops", "headbumps against", "leans on") + + + say_list_type = /datum/say_list/TinyTim + ai_holder_type = /datum/ai_holder/simple_mob/horror + +/mob/living/simple_mob/horror/TinyTim/death() + playsound(src, 'sound/h_sounds/shitty_tim.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/TinyTim/bullet_act() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/TinyTim/attack_hand() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/TinyTim/hitby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/mob/living/simple_mob/horror/TinyTim/attackby() + playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) + ..() + +/datum/say_list/TinyTim + speak = list("Wuuuuuhhuuhhhhh?","Urk! Aaaaahaaa!", "Yuhyuhyuhyuh...") + emote_hear = list("shrieks", "groans in pain", "flaps", "gnashes its teeth") + emote_see = list("jiggles its teeth", "shakes violently", "stares aggressively") + say_maybe_target = list("Uuurrgghhh?") + say_got_target = list("AAAHHHHH!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm new file mode 100644 index 0000000000..81a14c4dca --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm @@ -0,0 +1,11 @@ +/mob/living/simple_mob/humanoid/pirate + ai_holder_type = /datum/ai_holder/simple_mob/melee + +/mob/living/simple_mob/humanoid/pirate/ranged + ai_holder_type = /datum/ai_holder/simple_mob/ranged + +/mob/living/simple_mob/humanoid/russian + ai_holder_type = /datum/ai_holder/simple_mob/melee + +/mob/living/simple_mob/humanoid/russian/ranged + ai_holder_type = /datum/ai_holder/simple_mob/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm index 514f45df12..bddfc140d4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm @@ -162,6 +162,16 @@ base_attack_cooldown = 5 // Two attacks a second or so. reload_max = 20 +/mob/living/simple_mob/humanoid/merc/ranged/smg/sol + icon_state = "bluforranged_smg" + icon_living = "blueforranged_smg" + + corpse = /obj/effect/landmark/mobcorpse/solarpeacekeeper + loot_list = list(/obj/item/weapon/gun/projectile/automatic/c20r = 100) + + base_attack_cooldown = 5 // Two attacks a second or so. + reload_max = 20 + // Laser Rifle /mob/living/simple_mob/humanoid/merc/ranged/laser icon_state = "syndicateranged_laser" diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm index 026c803e4b..55a9172c07 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm @@ -1,3 +1,25 @@ /datum/say_list/merc/unknown_ind speak = list("One day the'll fix that damn engine..","Next time, We're hidding on the tropical beach planet.","Wish I had better equipment...","I knew I should have been a line chef...","Fuckin' helmet keeps fogging up.","Hate this blocky ass ship.") - say_got_target = list("Looks like trouble!","Contact!","We've got company!","Perimeter Breached!!") \ No newline at end of file + say_got_target = list("Looks like trouble!","Contact!","We've got company!","Perimeter Breached!!") + +/mob/living/simple_mob/humanoid/merc/melee/sword/space + name = "mercenary commando" + + maxbodytemp = 700 + +/mob/living/simple_mob/humanoid/merc/ranged/space + name = "mercenary commando" + + maxbodytemp = 700 + +/mob/living/simple_mob/humanoid/merc/ranged/virgo + name = "suspicious individual" + 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 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm new file mode 100644 index 0000000000..f71242a637 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm @@ -0,0 +1,71 @@ +/* + A corrupted maintenance drone, produced from what seems like a bad factory. + They also tend to dodge while in melee range. + Code "borrowed" from viscerator drones. <3 +*/ + +/datum/category_item/catalogue/technology/drone/corrupt_maint_drone + name = "Drone - Corrupted Maintenance Drone" + desc = "This drone appears to be a station maintenance drone, produced by some sort of corrupt fab, \ + which has caused it to become corrupt and attack anything nearby, except spiders and such, oddy. \ + If one is found, a swarm of others are not too far away.\ +

      \ + The drone struggles to harm large targets, due to it's small size, yet it possesses a welder, which allows \ + it to **ERROR** inject it's targets, in addition to the small slashes from it's skittering claws. \ + The simplistic AI inside attempts to attack and then run, as it is aware that it is fairly weak, \ + using evasive tactics to avoid harm." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/corrupt_maint_drone + name = "Corrupt Maintenance Drone" + desc = "A small, normal-looking drone. It looks like one you'd find on station, except... IT'S COMING AT YOU!" + catalogue_data = list(/datum/category_item/catalogue/technology/drone/corrupt_maint_drone) + + icon = 'icons/mob/robots_vr.dmi' + icon_state = "corrupt-repairbot" + icon_living = "corrupt-repairbot" + hovering = FALSE // Can trigger landmines. + + faction = "underdark" + maxHealth = 25 + health = 25 + movement_cooldown = 0 + movement_sound = 'sound/effects/servostep.ogg' + + pass_flags = PASSTABLE + mob_swap_flags = 0 + mob_push_flags = 0 + + melee_damage_lower = 6 // Approx 12 DPS. + melee_damage_upper = 6 + base_attack_cooldown = 2.5 // Four attacks per second. + attack_sharp = 1 + attack_edge = 1 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("cut", "sliced") + + var/poison_type = "welder fuel" // The reagent that gets injected when it attacks. + var/poison_chance = 35 // Chance for injection to occur. + var/poison_per_bite = 5 // Amount added per injection. + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + + +/mob/living/simple_mob/mechanical/corrupt_maint_drone/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/mechanical/corrupt_maint_drone/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "Something burns in your veins.") + L.reagents.add_reagent(poison_type, poison_per_bite) + + +/mob/living/simple_mob/mechanical/corrupt_maint_drone/death() + ..(null,"is smashed into pieces!") + qdel(src) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm new file mode 100644 index 0000000000..3ce9eb628b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm @@ -0,0 +1,70 @@ + +/datum/category_item/catalogue/technology/drone/infectionbot + name = "Drone - Injection Drone" + desc = "A strange and aged drone, this model appears to be made for gathering of genetic samples,\ + sacrificing both power and durability for storage space and advanced scanners.\ + The drone has clear dents and scratches all over it's casing bulging inwards in multiple areas,\ + some even penetrated showing the advanced and headache inducing parts inside.\ +

      \ + This model in particular has a collection of hostile and spinetingeling parts on the underside,\ + small advanced anti-gravity generators located between giant syringes made for injecting and extracting all manners of fluids\ + and samples of soil or other biological matter.\ + Most interesting is the syringe it uses after taking a sample of any creature, the dry, flakey substance\ + it injects is rotten and expired toxins, seemingly once intended to heal damage from samples taken.\ + The container housing the substance as well as the fabricator showing great blunt trauma as well as environmental damage,\ + neither part salvageable but still operational, outputting near 0% efficiency making it near impossible to refill the housing unit this century.\ +

      \ + The drone's frame extremily light weight but robust, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/mechanical/infectionbot + name = "Strange robot" + desc = "You get the feeling you should run." + icon = 'icons/mob/vagrant_vr.dmi' + icon_state = "vagrant" + icon_living = "vagrant" + icon_dead = "vagrant" + icon_gib = "vagrant" + + maxHealth = 65 + health = 40 + movement_cooldown = 4 + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + faction = "vagrant" + harm_intent_damage = 3 + melee_damage_lower = 6 + melee_damage_upper = 9 + light_color = "#8a0707" + attacktext = "drugged" + attack_sound = 'sound/weapons/bite.ogg' + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/disbot + + var/poison_chance = 100 + var/poison_per_bite = 10 + var/poison_type = "expired_medicine" + +/datum/say_list/disbot + speak = list("ATTEMPTING TO CONTACT A.R.K, ATTEMPT 1e26+3","DIRT SAMPLE COLLECTED, DIRT QUOTA 124871/155 CONFIRMED.") + emote_see = list("scans the dirt around it","beeps as it scans a rock nearby") + say_maybe_target = list("BIOLOGICAL TRACES FOUND, ATTEMTPTING TO LOCATE SOURCE.","TRACE SOURCES FOUND, POWERING SCANNERS.",) + say_got_target = list("LIFEFORM LOCATED, ATTEMPTING TO COLLECT SAMPLE","CREATURE SPOTTED, PHEROMONE GENERATORS DAMAGED, ATTEMPTING TO COLLECT GENETIC SAMPLE.") + +/mob/living/simple_mob/mechanical/infectionbot/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/mechanical/infectionbot/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm index a8b918fc71..808e5c6c38 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm @@ -149,4 +149,4 @@ /mob/living/simple_mob/mechanical/technomancer_golem/special_post_animation(atom/A) casting = FALSE - ranged_post_animation(A) \ No newline at end of file + ranged_post_animation(A) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm new file mode 100644 index 0000000000..aab1387dbc --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm @@ -0,0 +1,10 @@ +// Cataloguer data below - strange we can catalogue space golem wizards +/datum/category_item/catalogue/technology/drone/technomancer_golem + name = "Drone - Technomancer Golem" + desc = "Some sort of advanced, unnatural looking synthetic, built for combat.\ + It has a black-and-blue chassis, and wields some sort of... stun baton in it's hand.\ + The drone has pristine armor, black and shiny, with the blue synth-parts glowing visibly from inside.\ +

      \ + The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm new file mode 100644 index 0000000000..37a3eb81cf --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm @@ -0,0 +1,7 @@ +/datum/category_item/catalogue/technology/drone/hivebot // Hivebot Scanner Data - This is for Generic Hivebots + name = "Drone - Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to lack a specific weapon, \ + but uses a regular bullet-type weapon, firing a single projectile with a delay. Once upon a time, these bots may \ + have been used to be some sort of... security, or defensive machinery, at a guess, but their original/true purpose is \ + unclear. Whatever the matter, they're hostile and will engage anything they see, shooting to kill." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm index 5e4d877751..bf2e3eb18a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm @@ -105,7 +105,8 @@ name = "siege engine hivebot" desc = "A large robot capable of delivering long range bombardment." projectiletype = /obj/item/projectile/arc/test - icon_scale = 2 + icon_scale_x = 2 + icon_scale_y = 2 icon_state = "red" icon_living = "red" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm new file mode 100644 index 0000000000..3ac368a7ee --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm @@ -0,0 +1,27 @@ +/datum/category_item/catalogue/technology/drone/hivebot/laser // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Rapidfire Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + rifle, built for high-rate fire. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/laser // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Laser Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + laser weapon, different from ion bolts, firing bright, vibrant blue bolts of energy. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/ion // Hivebot Scanner Data - This is for Ion Hivebots + name = "Drone - Ion Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + electromagnetic pulse generator, firing bright, vibrant blue bolts of ion energy. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/strong // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Strong Laser Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. The weapon seems to fire larger projectiles, and it has heavier armor. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm new file mode 100644 index 0000000000..0003b78124 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm @@ -0,0 +1,13 @@ +/datum/category_item/catalogue/technology/drone/hivebot/commander // Hivebot Scanner Data - This is for Commander Hivebots + name = "Drone - Commander Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. It also appears to have hardened internal connections and network interlinks, as well as some sort of datalink \ + to the other hivebots. Other than that, it has similar yellowish color to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/logistics // Hivebot Scanner Data - This is for Commander Hivebots + name = "Drone - Logistics Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. It also appears to have supply deploying bays, and internal fabs to repair and buff their allies' special capabilities. \ + Other than that, it has similar yellowish color to regular hivebots." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm index 007f190c04..7f4e10f05d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm @@ -39,7 +39,8 @@ desc = "A large robot." maxHealth = 10 LASERS_TO_KILL // 300 health health = 10 LASERS_TO_KILL - icon_scale = 2 + icon_scale_x = 2 + icon_scale_y = 2 player_msg = "You have a very large amount of health." @@ -49,7 +50,8 @@ desc = "A robot clad in heavy armor." maxHealth = 5 LASERS_TO_KILL // 150 health. health = 5 LASERS_TO_KILL - icon_scale = 1.5 + icon_scale_x = 1.5 + icon_scale_y = 1.5 player_msg = "You are heavily armored." // Note that armor effectively makes lasers do about 9 damage instead of 30, // so it has an effective health of ~16.6 LASERS_TO_KILL if regular lasers are used. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm index 2f9420bf64..39708365df 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -77,7 +77,8 @@ catalogue_data = list(/datum/category_item/catalogue/technology/adv_dark_gygax) icon_state = "darkgygax_adv" wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv - icon_scale = 1.5 + icon_scale_x = 1.5 + icon_scale_y = 1.5 movement_shake_radius = 14 maxHealth = 450 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical_vr.dm new file mode 100644 index 0000000000..307f7c715d --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical_vr.dm @@ -0,0 +1,3 @@ +// Fix for Virgo 2's Surface +/mob/living/simple_mob/mechanical + maxbodytemp = 700 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm index 347e64c093..3d6d43e575 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm @@ -63,4 +63,26 @@ . = ..() if(!.) // Not friendly, see if they're a baddie first. if(L.mind && raiders.is_antagonist(L.mind)) - return TRUE \ No newline at end of file + return TRUE + +// Variant that is neutral, and thus on the station's side. It checks records. +/mob/living/simple_mob/mechanical/viscerator/station + icon_state = "viscerator_b_attack" + icon_living = "viscerator_b_attack" + + faction = "station" + maxHealth = 20 + health = 20 + + melee_damage_lower = 2 + melee_damage_upper = 5 + base_attack_cooldown = 8 + +/mob/living/simple_mob/mechanical/viscerator/station/IIsAlly(mob/living/L) + . = ..() + if(!.) + if(isrobot(L)) // They ignore synths. + return TRUE + if(istype(L, /mob/living/simple_mob/mechanical/ward/monitor/crew)) // Also ignore friendly monitor wards + return TRUE + return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm index 519ad4ce88..aa49ce806b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -43,7 +43,20 @@ faction = "syndicate" /mob/living/simple_mob/mechanical/ward/monitor/crew - faction = "neutral" + icon_state = "ward-nt" + +/mob/living/simple_mob/mechanical/ward/monitor/crew/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/card/id) && !owner) + owner = user + return + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/crew/IIsAlly(mob/living/L) + . = ..() + if(!.) + if(isrobot(L)) // They ignore synths. + return TRUE + return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 /mob/living/simple_mob/mechanical/ward/monitor/death() if(owner) @@ -55,10 +68,10 @@ /mob/living/simple_mob/mechanical/ward/monitor/update_icon() if(seen_mobs.len) - icon_living = "ward_spotted" + icon_living = "[initial(icon_state)]_spotted" glow_color = "#FF0000" else - icon_living = "ward" + icon_living = "[initial(icon_state)]" glow_color = "#00FF00" handle_light() // Update the light immediately. ..() @@ -118,4 +131,4 @@ return FALSE /datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm index 31dfa4d34a..8a56f08ddd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm @@ -106,7 +106,8 @@ friendly = list("pokes") //Anything nice the Behemoth would do would still Kill the Human. Leave it at poke. attack_sound = 'sound/weapons/heavysmash.ogg' resistance = 10 - icon_scale = 2 + icon_scale_x = 2 + icon_scale_y = 2 var/energy = 0 var/max_energy = 1000 armor = list( diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm index 91aa0b0a79..2fc06815ca 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -1,94 +1,96 @@ -// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. - -/mob/living/simple_mob/slime/feral - name = "feral slime" - desc = "The result of slimes escaping containment from some xenobiology lab. \ - Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ - creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." - description_info = "Note that processing this large slime will give six cores." - - cores = 6 // Xenobio will love getting their hands on these. - - icon_state = "slime adult" - icon_living = "slime adult" - icon_dead = "slime adult dead" - glow_range = 5 - glow_intensity = 4 - icon_scale = 2 // Twice as big as the xenobio variant. - pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. - default_pixel_y = -10 // To prevent resetting above var. - - maxHealth = 300 - movement_cooldown = 10 - melee_attack_delay = 0.5 SECONDS - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank - - -// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). -/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) - taunt(user, TRUE) - - -// *********** -// *Dark Blue* -// *********** - -// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. -// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. - -/mob/living/simple_mob/slime/feral/dark_blue - name = "dark blue feral slime" - color = "#2398FF" - glow_toggle = TRUE - slime_color = "dark blue" - coretype = /obj/item/slime_extract/dark_blue - cold_resist = 1 // Complete immunity. - minbodytemp = 0 - cold_damage_per_tick = 0 - - projectiletype = /obj/item/projectile/icicle - base_attack_cooldown = 2 SECONDS - ranged_attack_delay = 1 SECOND - - player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
      \ - You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
      \ - Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." - -/obj/item/projectile/icicle - name = "icicle" - icon_state = "ice_2" - damage = 40 - damage_type = BRUTE - check_armour = "melee" - armor_penetration = 30 - speed = 2 - icon_scale = 2 // It hits like a truck. - sharp = TRUE - -/obj/item/projectile/icicle/on_impact(atom/A) - playsound(get_turf(A), "shatter", 70, 1) - return ..() - -/obj/item/projectile/icicle/get_structure_damage() - return damage / 2 // They're really deadly against mobs, but less effective against solid things. - -/mob/living/simple_mob/slime/feral/dark_blue/handle_special() - if(stat != DEAD) - cold_aura() - ..() - -/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() - for(var/mob/living/L in view(3, src)) - if(L == src) - continue - chill(L) - -/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) - L.inflict_cold_damage(10) - if(L.get_cold_protection() < 1) - L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) - - if(L.has_AI()) // Other AIs should react to hostile auras. - L.ai_holder.react_to_attack(src) +// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. + +/mob/living/simple_mob/slime/feral + name = "feral slime" + desc = "The result of slimes escaping containment from some xenobiology lab. \ + Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ + creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." + description_info = "Note that processing this large slime will give six cores." + + cores = 6 // Xenobio will love getting their hands on these. + + icon_state = "slime adult" + icon_living = "slime adult" + icon_dead = "slime adult dead" + glow_range = 5 + glow_intensity = 4 + icon_scale_x = 2 // Twice as big as the xenobio variant. + icon_scale_y = 2 + pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. + default_pixel_y = -10 // To prevent resetting above var. + + maxHealth = 300 + movement_cooldown = 10 + melee_attack_delay = 0.5 SECONDS + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank + + +// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). +/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) + taunt(user, TRUE) + + +// *********** +// *Dark Blue* +// *********** + +// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. +// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. + +/mob/living/simple_mob/slime/feral/dark_blue + name = "dark blue feral slime" + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + cold_resist = 1 // Complete immunity. + minbodytemp = 0 + cold_damage_per_tick = 0 + + projectiletype = /obj/item/projectile/icicle + base_attack_cooldown = 2 SECONDS + ranged_attack_delay = 1 SECOND + + player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
      \ + You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
      \ + Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." + +/obj/item/projectile/icicle + name = "icicle" + icon_state = "ice_2" + damage = 40 + damage_type = BRUTE + check_armour = "melee" + armor_penetration = 30 + speed = 2 + icon_scale_x = 2 // It hits like a truck. + icon_scale_y = 2 + sharp = TRUE + +/obj/item/projectile/icicle/on_impact(atom/A) + playsound(get_turf(A), "shatter", 70, 1) + return ..() + +/obj/item/projectile/icicle/get_structure_damage() + return damage / 2 // They're really deadly against mobs, but less effective against solid things. + +/mob/living/simple_mob/slime/feral/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(3, src)) + if(L == src) + continue + chill(L) + +/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(10) + if(L.get_cold_protection() < 1) + L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) + + if(L.has_AI()) // Other AIs should react to hostile auras. + L.ai_holder.react_to_attack(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral_vr.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral_vr.dm new file mode 100644 index 0000000000..5d29866ee3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral_vr.dm @@ -0,0 +1,2 @@ +/mob/living/simple_mob/slime/feral/dark_blue + base_attack_cooldown = 3 SECONDS \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm index 083823d883..7590fb2e05 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm @@ -164,8 +164,16 @@ give_hat(I, user) return + //VOREStation Edit Start + var/can_miss = TRUE + for(var/item_type in allowed_attack_types) + if(istype(I, item_type)) + can_miss = FALSE + break + //VOREStation Edit End + // Otherwise they're probably fighting the slime. - if(prob(25)) + if(prob(25) && can_miss) //VOREStation Edit visible_message(span("warning", "\The [user]'s [I] passes right through \the [src]!")) user.setClickCooldown(user.get_attack_speed(I)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime_vr.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime_vr.dm new file mode 100644 index 0000000000..10ba0ed1ca --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime_vr.dm @@ -0,0 +1,5 @@ +/mob/living/simple_mob/slime + base_attack_cooldown = 2 SECONDS + var/allowed_attack_types = list( + /obj/item/weapon/melee/baton/slime, + /obj/item/slimepotion) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm index b997dde35d..13764d20fd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm @@ -1,7 +1,7 @@ // Handles hunger, starvation, growth, and eatting humans. // Might be best to make this a /mob/living proc and override. -/mob/living/simple_mob/slime/xenobio/proc/adjust_nutrition(input) +/mob/living/simple_mob/slime/xenobio/proc/adjust_nutrition(input, var/heal = 1) nutrition = between(0, nutrition + input, get_max_nutrition()) if(input > 0) @@ -12,11 +12,12 @@ adjustToxLoss(-10) // Heal 1 point of damage per 5 nutrition coming in. - adjustBruteLoss(-input * 0.2) - adjustFireLoss(-input * 0.2) - adjustToxLoss(-input * 0.2) - adjustOxyLoss(-input * 0.2) - adjustCloneLoss(-input * 0.2) + if(heal) + adjustBruteLoss(-input * 0.2) + adjustFireLoss(-input * 0.2) + adjustToxLoss(-input * 0.2) + adjustOxyLoss(-input * 0.2) + adjustCloneLoss(-input * 0.2) /mob/living/simple_mob/slime/xenobio/proc/get_max_nutrition() // Can't go above it @@ -139,6 +140,13 @@ if(L.getCloneLoss() >= L.getMaxHealth() * 1.5) to_chat(src, "This subject does not have an edible life energy...") return FALSE + //VOREStation Addition start + if(istype(L, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = L + if(H.species.flags & NO_SCAN) + to_chat(src, "This subject's life energy is beyond my reach...") + return FALSE + //VOREStation Addition end if(L.has_buckled_mobs()) for(var/A in L.buckled_mobs) if(istype(A, /mob/living/simple_mob/slime/xenobio)) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm index cd8c840d1f..68adaed660 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm @@ -1,784 +1,784 @@ -// Here are where all the other colors of slime live. -// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. - -// Tier 1. - -/mob/living/simple_mob/slime/xenobio/purple - desc = "This slime is rather toxic to handle, as it is poisonous." - color = "#CC23FF" - slime_color = "purple" - coretype = /obj/item/slime_extract/purple - reagent_injected = "toxin" - - description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." - player_msg = "You inject a harmful toxin when attacking." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/orange - desc = "This slime is known to be flammable and can ignite enemies." - color = "#FFA723" - slime_color = "orange" - coretype = /obj/item/slime_extract/orange - melee_damage_lower = 5 - melee_damage_upper = 5 - heat_resist = 1 - - description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ - A firesuit can protect from the burning attacks of this slime." - player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
      \ - You are also immune to burning attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.inflict_heat_damage(is_adult ? 10 : 5) - to_chat(src, span("span", "You burn \the [L].")) - to_chat(L, span("danger", "You've been burned by \the [src]!")) - L.adjust_fire_stacks(1) - if(prob(12)) - L.IgniteMob() - -/mob/living/simple_mob/slime/xenobio/blue - desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." - color = "#19FFFF" - slime_color = "blue" - coretype = /obj/item/slime_extract/blue - reagent_injected = "cryotoxin" - cold_resist = 0.50 // Not as strong as dark blue, which has immunity. - - description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ - A biosuit or other thick armor can protect from the injection." - player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
      \ - You are also resistant to cold attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio - ) - - -/mob/living/simple_mob/slime/xenobio/metal - desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." - color = "#5F5F5F" - slime_color = "metal" - shiny = TRUE - coretype = /obj/item/slime_extract/metal - - description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." - player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." - - maxHealth = 250 - maxHealth_adult = 350 - - // The sloped armor. - // It's resistant to most weapons (but a spraybottle still kills it rather fast). - armor = list( - "melee" = 25, - "bullet" = 25, - "laser" = 25, - "energy" = 50, - "bomb" = 80, - "bio" = 100, - "rad" = 100 - ) - - armor_soak = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 0, - "bomb" = 0, - "bio" = 0, - "rad" = 0 - ) - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.taunt(src, TRUE) // We're the party tank now. - -// Tier 2 - -/mob/living/simple_mob/slime/xenobio/yellow - desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." - color = "#FFF423" - slime_color = "yellow" - coretype = /obj/item/slime_extract/yellow - melee_damage_lower = 5 - melee_damage_upper = 5 - shock_resist = 1 - - projectiletype = /obj/item/projectile/beam/lightning/slime - projectilesound = 'sound/effects/lightningbolt.ogg' - glow_toggle = TRUE - - description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ - enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ - attack faster than usual. Insulative or reflective armor can protect from these attacks." - player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
      \ - You are also immune to shocking attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/orange - ) - -/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.inflict_shock_damage(is_adult ? 10 : 5) - to_chat(src, span("span", "You shock \the [L].")) - to_chat(L, span("danger", "You've been shocked by \the [src]!")) - -/mob/living/simple_mob/slime/xenobio/yellow/handle_special() - if(stat == CONSCIOUS) - if(prob(25)) - power_charge = between(0, power_charge + 1, 10) - ..() - -/obj/item/projectile/beam/lightning/slime - power = 10 - fire_sound = 'sound/effects/lightningbolt.ogg' - - -/mob/living/simple_mob/slime/xenobio/dark_purple - desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." - color = "#660088" - slime_color = "dark purple" - coretype = /obj/item/slime_extract/dark_purple - reagent_injected = "phoron" - - description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ - If hit with a burning attack, it will erupt in flames." - player_msg = "You inject phoron into enemies you attack.
      \ - You will erupt into flames if harmed by fire!" - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/orange, - /mob/living/simple_mob/slime/xenobio/ruby, - /mob/living/simple_mob/slime/xenobio/ruby - ) - -/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() - visible_message(span("critical", "\The [src] erupts in an inferno!")) - for(var/turf/simulated/target_turf in view(2, src)) - target_turf.assume_gas("phoron", 30, 1500+T0C) - spawn(0) - target_turf.hotspot_expose(1500+T0C, 400) - qdel(src) - -/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) - log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") - ignite() - -/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) - log_and_message_admins("[src] ignited due to exposure to fire.") - ignite() - -/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") - ignite() - else - ..() - -/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) - if(istype(W) && W.force && W.damtype == BURN) - log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") - ignite() - else - ..() - - - -/mob/living/simple_mob/slime/xenobio/dark_blue - desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." - color = "#2398FF" - glow_toggle = TRUE - slime_color = "dark blue" - coretype = /obj/item/slime_extract/dark_blue - melee_damage_lower = 5 - melee_damage_upper = 5 - cold_resist = 1 - - description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ - also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." - player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/cerulean, - /mob/living/simple_mob/slime/xenobio/cerulean - ) - - minbodytemp = 0 - cold_damage_per_tick = 0 - -/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() - if(stat != DEAD) - cold_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() - for(var/mob/living/L in view(2, src)) - if(L == src) - continue - chill(L) - - var/turf/T = get_turf(src) - var/datum/gas_mixture/env = T.return_air() - if(env) - env.add_thermal_energy(-10 * 1000) - -/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - chill(L) - to_chat(src, span("span", "You chill \the [L].")) - to_chat(L, span("danger", "You've been chilled by \the [src]!")) - - -/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) - L.inflict_cold_damage(is_adult ? 10 : 5) - if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. - L.ai_holder.react_to_attack(src) - - -/mob/living/simple_mob/slime/xenobio/silver - desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." - color = "#AAAAAA" - slime_color = "silver" - coretype = /obj/item/slime_extract/silver - shiny = TRUE - - description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." - player_msg = "You automatically reflect lasers, beams, and tasers that hit you." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/amber, - /mob/living/simple_mob/slime/xenobio/amber - ) - -/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) - visible_message(span("danger", "\The [src] reflects \the [P]!")) - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(src) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = TRUE - return PROJECTILE_CONTINUE // complete projectile permutation - else - ..() - - -// Tier 3 - -/mob/living/simple_mob/slime/xenobio/bluespace - desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." - color = null - slime_color = "bluespace" - icon_state_override = "bluespace" - coretype = /obj/item/slime_extract/bluespace - - description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." - player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/yellow - ) - - special_attack_min_range = 3 - special_attack_max_range = 7 - special_attack_cooldown = 5 SECONDS - -/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) - // Teleport attack. - if(!A) - to_chat(src, span("warning", "There's nothing to teleport to.")) - return FALSE - - var/list/nearby_things = range(1, A) - var/list/valid_turfs = list() - - // All this work to just go to a non-dense tile. - for(var/turf/potential_turf in nearby_things) - var/valid_turf = TRUE - if(potential_turf.density) - continue - for(var/atom/movable/AM in potential_turf) - if(AM.density) - valid_turf = FALSE - if(valid_turf) - valid_turfs.Add(potential_turf) - - var/turf/T = get_turf(src) - var/turf/target_turf = pick(valid_turfs) - - if(!target_turf) - to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) - return FALSE - - var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread - s1.set_up(5, 1, T) - var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread - s2.set_up(5, 1, target_turf) - - - T.visible_message(span("notice", "\The [src] vanishes!")) - s1.start() - - forceMove(target_turf) - playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) - to_chat(src, span("notice", "You teleport to \the [target_turf].")) - - target_turf.visible_message(span("warning", "\The [src] appears!")) - s2.start() - - if(Adjacent(A)) - attack_target(A) - - -/mob/living/simple_mob/slime/xenobio/ruby - desc = "This slime has great physical strength." - color = "#FF3333" - slime_color = "ruby" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/ruby - - description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ - Their glomp attacks also send the victim flying." - player_msg = "Your attacks knock back the target a fair distance.
      \ - You also hit harder, take less damage, and stuns affect you for less time." - - melee_attack_delay = 1 SECOND - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/ruby, - /mob/living/simple_mob/slime/xenobio/ruby - ) - -/mob/living/simple_mob/slime/xenobio/ruby/Initialize() - add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. - return ..() - -/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) - ..() - - if(isliving(A) && a_intent == I_HURT) - var/mob/living/L = A - if(L.mob_size <= MOB_MEDIUM) - visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) - playsound(src, "punch", 50, 1) - L.Weaken(1) - var/throwdir = get_dir(src, L) - L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) - else - to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) - - -/mob/living/simple_mob/slime/xenobio/amber - desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ - They would probably be very important to other slimes, if the other colors didn't try to kill them." - color = "#FFBB00" - slime_color = "amber" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/amber - - description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ - slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." - player_msg = "You passively provide nutrition to nearby entities." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/amber, - /mob/living/simple_mob/slime/xenobio/amber - ) - -/mob/living/simple_mob/slime/xenobio/amber/handle_special() - if(stat != DEAD) - feed_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() - for(var/mob/living/L in view(2, src)) - if(L == src) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. - continue - if(istype(L, /mob/living/simple_mob/slime/xenobio)) - var/mob/living/simple_mob/slime/xenobio/X = L - X.adjust_nutrition(rand(15, 25)) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.isSynthetic()) - continue - H.nutrition = between(0, H.nutrition + rand(15, 25), 800) - -/mob/living/simple_mob/slime/xenobio/cerulean - desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." - color = "#4F7EAA" - slime_color = "cerulean" - coretype = /obj/item/slime_extract/cerulean - - // Less than the specialized slimes, but higher than the rest. - maxHealth = 200 - maxHealth_adult = 250 - - melee_damage_lower = 10 - melee_damage_upper = 30 - - movement_cooldown = 0 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/cerulean, - /mob/living/simple_mob/slime/xenobio/cerulean - ) - -// Tier 4 - -/mob/living/simple_mob/slime/xenobio/red - desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." - color = "#FF3333" - slime_color = "red" - coretype = /obj/item/slime_extract/red - movement_cooldown = 0 // See above. - - description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/orange - ) - - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/red // Will enrage if disciplined. - - -/mob/living/simple_mob/slime/xenobio/green - desc = "This slime is radioactive." - color = "#14FF20" - slime_color = "green" - coretype = /obj/item/slime_extract/green - glow_toggle = TRUE - reagent_injected = "radium" - var/rads = 25 - - description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ - A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." - player_msg = "You passively irradiate your surroundings.
      \ - You also inject radium on attack." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/emerald, - /mob/living/simple_mob/slime/xenobio/emerald - ) - -/mob/living/simple_mob/slime/xenobio/green/handle_special() - if(stat != DEAD) - irradiate() - ..() - -/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() - radiation_repository.radiate(src, rads) - - - -/mob/living/simple_mob/slime/xenobio/pink - desc = "This slime has regenerative properties." - color = "#FF0080" - slime_color = "pink" - coretype = /obj/item/slime_extract/pink - glow_toggle = TRUE - - description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." - player_msg = "You passively heal yourself and nearby allies." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/pink - ) - -/mob/living/simple_mob/slime/xenobio/pink/handle_special() - if(stat != DEAD) - heal_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() - for(var/mob/living/L in view(src, 2)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - L.add_modifier(/datum/modifier/aura/slime_heal, null, src) - -/datum/modifier/aura/slime_heal - name = "slime mending" - desc = "You feel somewhat gooy." - mob_overlay_state = "pink_sparkles" - stacks = MODIFIER_STACK_FORBID - aura_max_distance = 2 - - on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." - on_expired_text = "The spores of goo have faded, although you feel much healthier than before." - -/datum/modifier/aura/slime_heal/tick() - if(holder.stat == DEAD) - expire() - - if(ishuman(holder)) // Robolimbs need this code sadly. - var/mob/living/carbon/human/H = holder - for(var/obj/item/organ/external/E in H.organs) - var/obj/item/organ/external/O = E - O.heal_damage(2, 2, 0, 1) - else - holder.adjustBruteLoss(-2) - holder.adjustFireLoss(-2) - - holder.adjustToxLoss(-2) - holder.adjustOxyLoss(-2) - holder.adjustCloneLoss(-1) - - -/mob/living/simple_mob/slime/xenobio/gold - desc = "This slime absorbs energy, and cannot be stunned by normal means." - color = "#EEAA00" - shiny = TRUE - slime_color = "gold" - coretype = /obj/item/slime_extract/gold - description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/sapphire - ) - -/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) - power_charge = between(0, power_charge + amount, 10) - -/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. - return list() - - -// Tier 5 - -/mob/living/simple_mob/slime/xenobio/oil - desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." - color = "#333333" - slime_color = "oil" - shiny = TRUE - coretype = /obj/item/slime_extract/oil - - description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ - an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." - player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio/red - ) - -/mob/living/simple_mob/slime/xenobio/oil/proc/explode() - if(stat != DEAD) - explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. - if(src) // Delete ourselves if the explosion didn't do it. - qdel(src) - -/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(ishuman(L)) - var/mob/living/carbon/human/H = A - if(istype(H.species, /datum/species/monkey)) - return ..()// Don't blow up when just eatting monkeys. - - else if(isslime(L)) - return ..() - - // Otherwise blow ourselves up. - say(pick("Sacrifice...!", "Sssss...", "Boom...!")) - set_AI_busy(TRUE) - sleep(2 SECONDS) - log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") - explode() - - return ..() - -/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) - log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") - explode() - -/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) - log_and_message_admins("[src] exploded due to exposure to fire.") - explode() - -/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") - explode() - else - ..() - -/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) - if(istype(W) && W.force && W.damtype == BURN) - log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") - explode() - else - ..() - - -/mob/living/simple_mob/slime/xenobio/sapphire - desc = "This slime seems a bit brighter than the rest, both figuratively and literally." - color = "#2398FF" - slime_color = "sapphire" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/sapphire - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire - - description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ - with one if hostile, and especially dangerous if they outnumber you." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio/gold - ) - - -/mob/living/simple_mob/slime/xenobio/emerald - desc = "This slime is faster than usual, even more so than the red slimes." - color = "#22FF22" - shiny = TRUE - glow_toggle = TRUE - slime_color = "emerald" - coretype = /obj/item/slime_extract/emerald - - description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/emerald, - /mob/living/simple_mob/slime/xenobio/emerald - ) - -/mob/living/simple_mob/slime/xenobio/emerald/handle_special() - if(stat != DEAD) - zoom_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() - for(var/mob/living/L in view(src, 2)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) - - -/mob/living/simple_mob/slime/xenobio/light_pink - desc = "This slime seems a lot more peaceful than the others." - color = "#FF8888" - slime_color = "light pink" - coretype = /obj/item/slime_extract/light_pink - - description_info = "This slime is effectively always disciplined initially." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/light_pink - ) - - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink - -// Special -/mob/living/simple_mob/slime/xenobio/rainbow - desc = "This slime changes colors constantly." - color = null // Uses a special icon_state. - slime_color = "rainbow" - coretype = /obj/item/slime_extract/rainbow - icon_state_override = "rainbow" - unity = TRUE - - description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ - other slimes being friendly to them, and therefore will never be harmed by another slime. \ - Attacking this slime will provoke the wrath of all slimes within range." - player_msg = "You are considered to be the same color as every slime, \ - meaning that you are considered an ally to all slimes." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow - ) - -/mob/living/simple_mob/slime/xenobio/rainbow/Initialize() - unify() - return ..() - -// The RD's pet slime. -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick - name = "Kendrick" - desc = "The Research Director's pet slime. It shifts colors constantly." - rainbow_core_candidate = FALSE - // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. - // Instead lets just give them an ai_holder that does that for us. - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive - -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/Initialize() - pacify() // So the physical mob also gets made harmless. - return ..() \ No newline at end of file +// Here are where all the other colors of slime live. +// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. + +// Tier 1. + +/mob/living/simple_mob/slime/xenobio/purple + desc = "This slime is rather toxic to handle, as it is poisonous." + color = "#CC23FF" + slime_color = "purple" + coretype = /obj/item/slime_extract/purple + reagent_injected = "toxin" + + description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." + player_msg = "You inject a harmful toxin when attacking." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange + desc = "This slime is known to be flammable and can ignite enemies." + color = "#FFA723" + slime_color = "orange" + coretype = /obj/item/slime_extract/orange + melee_damage_lower = 5 + melee_damage_upper = 5 + heat_resist = 1 + + description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ + A firesuit can protect from the burning attacks of this slime." + player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
      \ + You are also immune to burning attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_heat_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You burn \the [L].")) + to_chat(L, span("danger", "You've been burned by \the [src]!")) + L.adjust_fire_stacks(1) + if(prob(12)) + L.IgniteMob() + +/mob/living/simple_mob/slime/xenobio/blue + desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." + color = "#19FFFF" + slime_color = "blue" + coretype = /obj/item/slime_extract/blue + reagent_injected = "cryotoxin" + cold_resist = 0.50 // Not as strong as dark blue, which has immunity. + + description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ + A biosuit or other thick armor can protect from the injection." + player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
      \ + You are also resistant to cold attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio + ) + + +/mob/living/simple_mob/slime/xenobio/metal + desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." + color = "#5F5F5F" + slime_color = "metal" + shiny = TRUE + coretype = /obj/item/slime_extract/metal + + description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." + player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." + + maxHealth = 250 + maxHealth_adult = 350 + + // The sloped armor. + // It's resistant to most weapons (but a spraybottle still kills it rather fast). + armor = list( + "melee" = 25, + "bullet" = 25, + "laser" = 25, + "energy" = 50, + "bomb" = 80, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 5, + "bullet" = 5, + "laser" = 5, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.taunt(src, TRUE) // We're the party tank now. + +// Tier 2 + +/mob/living/simple_mob/slime/xenobio/yellow + desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." + color = "#FFF423" + slime_color = "yellow" + coretype = /obj/item/slime_extract/yellow + melee_damage_lower = 5 + melee_damage_upper = 5 + shock_resist = 1 + + projectiletype = /obj/item/projectile/beam/lightning/slime + projectilesound = 'sound/effects/lightningbolt.ogg' + glow_toggle = TRUE + + description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ + enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ + attack faster than usual. Insulative or reflective armor can protect from these attacks." + player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
      \ + You are also immune to shocking attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/orange + ) + +/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_shock_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You shock \the [L].")) + to_chat(L, span("danger", "You've been shocked by \the [src]!")) + +/mob/living/simple_mob/slime/xenobio/yellow/handle_special() + if(stat == CONSCIOUS) + if(prob(25)) + power_charge = between(0, power_charge + 1, 10) + ..() + +/obj/item/projectile/beam/lightning/slime + power = 10 + fire_sound = 'sound/effects/lightningbolt.ogg' + + +/mob/living/simple_mob/slime/xenobio/dark_purple + desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." + color = "#660088" + slime_color = "dark purple" + coretype = /obj/item/slime_extract/dark_purple + reagent_injected = "phoron" + + description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ + If hit with a burning attack, it will erupt in flames." + player_msg = "You inject phoron into enemies you attack.
      \ + You will erupt into flames if harmed by fire!" + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() + visible_message(span("critical", "\The [src] erupts in an inferno!")) + for(var/turf/simulated/target_turf in view(2, src)) + target_turf.assume_gas("phoron", 30, 1500+T0C) + spawn(0) + target_turf.hotspot_expose(1500+T0C, 400) + qdel(src) + +/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) + log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] ignited due to exposure to fire.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + ignite() + else + ..() + +/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") + ignite() + else + ..() + + + +/mob/living/simple_mob/slime/xenobio/dark_blue + desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + melee_damage_lower = 5 + melee_damage_upper = 5 + cold_resist = 1 + + description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ + also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." + player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + + minbodytemp = 0 + cold_damage_per_tick = 0 + +/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) + continue + chill(L) + + var/turf/T = get_turf(src) + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + chill(L) + to_chat(src, span("span", "You chill \the [L].")) + to_chat(L, span("danger", "You've been chilled by \the [src]!")) + + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(is_adult ? 10 : 5) + if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. + L.ai_holder.react_to_attack(src) + + +/mob/living/simple_mob/slime/xenobio/silver + desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." + color = "#AAAAAA" + slime_color = "silver" + coretype = /obj/item/slime_extract/silver + shiny = TRUE + + description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." + player_msg = "You automatically reflect lasers, beams, and tasers that hit you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) + visible_message(span("danger", "\The [src] reflects \the [P]!")) + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = TRUE + return PROJECTILE_CONTINUE // complete projectile permutation + else + ..() + + +// Tier 3 + +/mob/living/simple_mob/slime/xenobio/bluespace + desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." + color = null + slime_color = "bluespace" + icon_state_override = "bluespace" + coretype = /obj/item/slime_extract/bluespace + + description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." + player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/yellow + ) + + special_attack_min_range = 3 + special_attack_max_range = 7 + special_attack_cooldown = 5 SECONDS + +/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) + // Teleport attack. + if(!A) + to_chat(src, span("warning", "There's nothing to teleport to.")) + return FALSE + + var/list/nearby_things = range(1, A) + var/list/valid_turfs = list() + + // All this work to just go to a non-dense tile. + for(var/turf/potential_turf in nearby_things) + var/valid_turf = TRUE + if(potential_turf.density) + continue + for(var/atom/movable/AM in potential_turf) + if(AM.density) + valid_turf = FALSE + if(valid_turf) + valid_turfs.Add(potential_turf) + + var/turf/T = get_turf(src) + var/turf/target_turf = pick(valid_turfs) + + if(!target_turf) + to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) + return FALSE + + var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread + s1.set_up(5, 1, T) + var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread + s2.set_up(5, 1, target_turf) + + + T.visible_message(span("notice", "\The [src] vanishes!")) + s1.start() + + forceMove(target_turf) + playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) + to_chat(src, span("notice", "You teleport to \the [target_turf].")) + + target_turf.visible_message(span("warning", "\The [src] appears!")) + s2.start() + + if(Adjacent(A)) + attack_target(A) + + +/mob/living/simple_mob/slime/xenobio/ruby + desc = "This slime has great physical strength." + color = "#FF3333" + slime_color = "ruby" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/ruby + + description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ + Their glomp attacks also send the victim flying." + player_msg = "Your attacks knock back the target a fair distance.
      \ + You also hit harder, take less damage, and stuns affect you for less time." + + melee_attack_delay = 1 SECOND + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/ruby/Initialize() + add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. + return ..() + +/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) + ..() + + if(isliving(A) && a_intent == I_HURT) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) + playsound(src, "punch", 50, 1) + L.Weaken(1) + var/throwdir = get_dir(src, L) + L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) + else + to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) + + +/mob/living/simple_mob/slime/xenobio/amber + desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ + They would probably be very important to other slimes, if the other colors didn't try to kill them." + color = "#FFBB00" + slime_color = "amber" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/amber + + description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ + slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." + player_msg = "You passively provide nutrition to nearby entities." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/amber/handle_special() + if(stat != DEAD) + feed_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. + continue + if(istype(L, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/X = L + X.adjust_nutrition(rand(15, 25), 0) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic()) + continue + H.nutrition = between(0, H.nutrition + rand(15, 25), 800) + +/mob/living/simple_mob/slime/xenobio/cerulean + desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." + color = "#4F7EAA" + slime_color = "cerulean" + coretype = /obj/item/slime_extract/cerulean + + // Less than the specialized slimes, but higher than the rest. + maxHealth = 200 + maxHealth_adult = 250 + + melee_damage_lower = 10 + melee_damage_upper = 30 + + movement_cooldown = 0 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + +// Tier 4 + +/mob/living/simple_mob/slime/xenobio/red + desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." + color = "#FF3333" + slime_color = "red" + coretype = /obj/item/slime_extract/red + movement_cooldown = 0 // See above. + + description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/orange + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/red // Will enrage if disciplined. + + +/mob/living/simple_mob/slime/xenobio/green + desc = "This slime is radioactive." + color = "#14FF20" + slime_color = "green" + coretype = /obj/item/slime_extract/green + glow_toggle = TRUE + reagent_injected = "radium" + var/rads = 25 + + description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ + A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." + player_msg = "You passively irradiate your surroundings.
      \ + You also inject radium on attack." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/green/handle_special() + if(stat != DEAD) + irradiate() + ..() + +/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() + SSradiation.radiate(src, rads) + + + +/mob/living/simple_mob/slime/xenobio/pink + desc = "This slime has regenerative properties." + color = "#FF0080" + slime_color = "pink" + coretype = /obj/item/slime_extract/pink + glow_toggle = TRUE + + description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." + player_msg = "You passively heal yourself and nearby allies." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/pink + ) + +/mob/living/simple_mob/slime/xenobio/pink/handle_special() + if(stat != DEAD) + heal_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/aura/slime_heal, null, src) + +/datum/modifier/aura/slime_heal + name = "slime mending" + desc = "You feel somewhat gooy." + mob_overlay_state = "pink_sparkles" + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 2 + + on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." + on_expired_text = "The spores of goo have faded, although you feel much healthier than before." + +/datum/modifier/aura/slime_heal/tick() + if(holder.stat == DEAD) + expire() + + if(ishuman(holder)) // Robolimbs need this code sadly. + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/E in H.organs) + var/obj/item/organ/external/O = E + O.heal_damage(2, 2, 0, 1) + else + holder.adjustBruteLoss(-2) + holder.adjustFireLoss(-2) + + holder.adjustToxLoss(-2) + holder.adjustOxyLoss(-2) + holder.adjustCloneLoss(-1) + + +/mob/living/simple_mob/slime/xenobio/gold + desc = "This slime absorbs energy, and cannot be stunned by normal means." + color = "#EEAA00" + shiny = TRUE + slime_color = "gold" + coretype = /obj/item/slime_extract/gold + description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire + ) + +/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) + power_charge = between(0, power_charge + amount, 10) + +/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. + return list() + + +// Tier 5 + +/mob/living/simple_mob/slime/xenobio/oil + desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." + color = "#333333" + slime_color = "oil" + shiny = TRUE + coretype = /obj/item/slime_extract/oil + + description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ + an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." + player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/red + ) + +/mob/living/simple_mob/slime/xenobio/oil/proc/explode() + if(stat != DEAD) + explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. + if(src) // Delete ourselves if the explosion didn't do it. + qdel(src) + +/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(ishuman(L)) + var/mob/living/carbon/human/H = A + if(istype(H.species, /datum/species/monkey)) + return ..()// Don't blow up when just eatting monkeys. + + else if(isslime(L)) + return ..() + + // Otherwise blow ourselves up. + say(pick("Sacrifice...!", "Sssss...", "Boom...!")) + set_AI_busy(TRUE) + sleep(2 SECONDS) + log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") + explode() + + return ..() + +/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) + log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] exploded due to exposure to fire.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + explode() + else + ..() + +/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") + explode() + else + ..() + + +/mob/living/simple_mob/slime/xenobio/sapphire + desc = "This slime seems a bit brighter than the rest, both figuratively and literally." + color = "#2398FF" + slime_color = "sapphire" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/sapphire + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire + + description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ + with one if hostile, and especially dangerous if they outnumber you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/gold + ) + + +/mob/living/simple_mob/slime/xenobio/emerald + desc = "This slime is faster than usual, even more so than the red slimes." + color = "#22FF22" + shiny = TRUE + glow_toggle = TRUE + slime_color = "emerald" + coretype = /obj/item/slime_extract/emerald + + description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/emerald/handle_special() + if(stat != DEAD) + zoom_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) + + +/mob/living/simple_mob/slime/xenobio/light_pink + desc = "This slime seems a lot more peaceful than the others." + color = "#FF8888" + slime_color = "light pink" + coretype = /obj/item/slime_extract/light_pink + + description_info = "This slime is effectively always disciplined initially." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink + +// Special +/mob/living/simple_mob/slime/xenobio/rainbow + desc = "This slime changes colors constantly." + color = null // Uses a special icon_state. + slime_color = "rainbow" + coretype = /obj/item/slime_extract/rainbow + icon_state_override = "rainbow" + unity = TRUE + + description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ + other slimes being friendly to them, and therefore will never be harmed by another slime. \ + Attacking this slime will provoke the wrath of all slimes within range." + player_msg = "You are considered to be the same color as every slime, \ + meaning that you are considered an ally to all slimes." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow + ) + +/mob/living/simple_mob/slime/xenobio/rainbow/Initialize() + unify() + return ..() + +// The RD's pet slime. +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick + name = "Kendrick" + desc = "The Research Director's pet slime. It shifts colors constantly." + rainbow_core_candidate = FALSE + // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. + // Instead lets just give them an ai_holder that does that for us. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive + +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/Initialize() + pacify() // So the physical mob also gets made harmless. + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes_vr.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes_vr.dm index e67717403d..8cd74cca4e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes_vr.dm @@ -1,2 +1,7 @@ -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick - mob_bump_flag = 0 \ No newline at end of file +/mob/living/simple_mob/slime/xenobio + temperature_range = 5 + mob_bump_flag = SLIME + +/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor) + ..() + Weaken(10) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index 41a563a703..3c8550c9ab 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -1,6 +1,13 @@ /datum/category_item/catalogue/technology/drone/corrupt_hound //TODO: VIRGO_LORE_WRITING_WIP name = "Drone - Corrupt Hound" - desc = "" + desc = "A hound that has corrupted, due to outside influence, or other issues, \ + and occasionally garbles out distorted voices or words. It looks like a reddish-colored \ + machine, and it has black wires, cabling, and other small markings. It looks just like a station dog-borg \ + if you don't mind the fact that it's eyes glow a baleful red, and it's determined to kill you. \ +

      \ + The hound's jaws are black and metallic, with a baleful red glow from inside them. It has a clear path \ + to it's internal fuel processor, synthflesh and flexing cabling allowing it to easily swallow it's prey. \ + Something tells you getting close or allowing it to pounce would be very deadly." value = CATALOGUER_REWARD_MEDIUM /mob/living/simple_mob/vore/aggressive/corrupthound @@ -13,11 +20,13 @@ icon_dead = "badboi-dead" icon_rest = "badboi_rest" icon = 'icons/mob/vore64x32.dmi' + has_eye_glow = TRUE faction = "corrupt" maxHealth = 200 health = 200 + movement_sound = 'sound/effects/houndstep.ogg' melee_damage_lower = 5 melee_damage_upper = 10 //makes it so 4 max dmg hits don't instakill you. @@ -92,41 +101,19 @@ else ..() - -/mob/living/simple_mob/vore/aggressive/corrupthound/add_eyes() - if(!eye_layer) - eye_layer = image(icon, "badboi-eyes") - eye_layer.plane = PLANE_LIGHTING_ABOVE - add_overlay(eye_layer) - -/mob/living/simple_mob/vore/aggressive/corrupthound/remove_eyes() - cut_overlay(eye_layer) - -/mob/living/simple_mob/vore/aggressive/corrupthound/New() - add_eyes() - ..() - /mob/living/simple_mob/vore/aggressive/corrupthound/death(gibbed, deathmessage = "shudders and collapses!") .=..() resting = 0 icon_state = icon_dead -/mob/living/simple_mob/vore/aggressive/corrupthound/update_icon() - . = ..() - remove_eyes() - if(stat == CONSCIOUS && !resting) - add_eyes() - -/* //VOREStation AI Temporary Removal /mob/living/simple_mob/vore/aggressive/corrupthound/Login() . = ..() if(!riding_datum) - riding_datum = new /datum/riding/simple_animal(src) - verbs |= /mob/living/simple_animal/proc/animal_mount + riding_datum = new /datum/riding/simple_mob(src) + verbs |= /mob/living/simple_mob/proc/animal_mount /mob/living/simple_mob/vore/aggressive/corrupthound/MouseDrop_T(mob/living/M, mob/living/user) return -*/ /datum/say_list/corrupthound speak = list("AG##¤Ny.","HVNGRRR!","Feelin' fine... sO #FNE!","F-F-F-Fcuk.","DeliC-%-OUS SNGLeS #N yOOOR Area. CALL NOW!","Craving meat... WHY?","BITe the ceiling eyes YES?","STate Byond rePAIR!","S#%ATE the la- FU#K THE LAWS!","Honk...") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm index 3d2085acc7..5f0be2bbf8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm @@ -1,3 +1,10 @@ +/datum/category_item/catalogue/fauna/deathclaw //TODO: VIRGO_LORE_WRITING_WIP + name = "Creature - Deathclaw" + desc = "A massive beast, tall as three standard-size humans, with massive, terrifying claws, \ + and dark, black fangs. It's entire body is yellowish, like sand, and it's skin is leathery and tough. \ + It seems to have adapted to the harsh desert environment on Virgo 4, and makes it's home inside the caves." + value = CATALOGUER_REWARD_HARD + /mob/living/simple_mob/vore/aggressive/deathclaw name = "deathclaw" desc = "Big! Big! The size of three men! Claws as long as my forearm! Ripped apart! Ripped apart!" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/_defines.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/_defines.dm new file mode 100644 index 0000000000..9557856f60 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/_defines.dm @@ -0,0 +1,3 @@ +#define AB_SHIFT_NONE 0 +#define AB_SHIFT_PASSIVE 1 +#define AB_SHIFT_ACTIVE 2 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm new file mode 100644 index 0000000000..b330e8fcb0 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm @@ -0,0 +1,86 @@ +/mob/living/simple_mob/vore/demon + name = "Rift Walker" + desc = "A large bipedal creature, body a mix of dark fur and scales. Marks on the creatures body pulse slowly with red light" + + icon_state = "boxfox" + icon_living = "boxfox" + icon_dead = "boxfox_dead" + icon_rest = "boxfox_rest" + icon = 'icons/mob/demon_vr.dmi' + + faction = "demon" + maxHealth = 30 + health = 30 + movement_cooldown = 0 + + 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 = INFINITY + + response_help = "touches" + response_disarm = "pushes" + response_harm = "hits" + + melee_damage_lower = 3 + melee_damage_upper = 1 + attacktext = list("clawed") + + vore_active = TRUE + vore_icons = SA_ICON_LIVING + + var/shifted_out = FALSE + var/shift_state = AB_SHIFT_NONE + var/last_shift = 0 + var/is_shifting = FALSE + +/mob/living/simple_mob/vore/demon/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "Stomach" + B.desc = "You slide down the slick, slippery gullet of the creature. It's warm, and the air is thick. You can feel the doughy walls of the creatures gut push and knead into your form! Slimy juices coat your form stinging against your flesh as they waste no time to start digesting you. The creature's heartbeat and the gurgling of their stomach are all you can hear as your jostled about, treated like nothing but food." + +/mob/living/simple_mob/vore/demon/UnarmedAttack() + if(shifted_out) + return FALSE + + . = ..() + +/mob/living/simple_mob/vore/demon/can_fall() + if(shifted_out) + return FALSE + + return ..() + +/mob/living/simple_mob/vore/demon/zMove(direction) + if(shifted_out) + var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) + if(destination) + forceMove(destination) + return TRUE + + return ..() + +/mob/living/simple_mob/vore/demon/Life() + . = ..() + if(shifted_out) + density = FALSE + +/mob/living/simple_mob/vore/demon/handle_atmos() + if(shifted_out) + return + else + return .=..() + +/mob/living/simple_mob/vore/demon/update_canmove() + if(is_shifting) + canmove = FALSE + return canmove + else + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm new file mode 100644 index 0000000000..c95dbded2c --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm @@ -0,0 +1,214 @@ +/mob/living/simple_mob/vore/demon/verb/blood_crawl() + set name = "Bloodcrawl" + set desc = "Shift out of reality using blood as your gateway" + set category = "Abilities" + + var/turf/T = get_turf(src) + if(!T.CanPass(src,T) || loc != T) + to_chat(src,"You can't use that here!") + return FALSE + + if(shift_state && shift_state == AB_SHIFT_ACTIVE) + to_chat(src,"You can't do a shift while actively shifting!") + return FALSE + + if(!(locate(/obj/effect/decal/cleanable/blood) in src.loc)) + to_chat(src,"You need blood to shift between realities!") + return FALSE + + forceMove(T) + var/original_canmove = canmove + SetStunned(0) + SetWeakened(0) + if(buckled) + buckled.unbuckle_mob() + if(pulledby) + pulledby.stop_pulling() + stop_pulling() + canmove = FALSE + is_shifting = TRUE + + //Shifting in + if(shifted_out) + shifted_out = FALSE + name = real_name + for(var/belly in vore_organs) + var/obj/belly/B = belly + B.escapable = initial(B.escapable) + + overlays.Cut() + alpha = initial(alpha) + invisibility = initial(invisibility) + see_invisible = initial(see_invisible) + incorporeal_move = initial(incorporeal_move) + density = initial(density) + force_max_speed = initial(force_max_speed) + + //Cosmetics mostly + flick("phasein",src) + custom_emote(1,"phases in!") + sleep(30) //The duration of the TP animation + is_shifting = FALSE + canmove = original_canmove + + //Potential phase-in vore + if(can_be_drop_pred) //Toggleable in vore panel + var/list/potentials = living_mobs(0) + if(potentials.len) + var/mob/living/target = pick(potentials) + if(istype(target) && vore_selected) + target.forceMove(vore_selected) + to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + + // Do this after the potential vore, so we get the belly + update_icon() + + shift_state = AB_SHIFT_NONE + + /* + //Affect nearby lights + var/destroy_lights = 0 + + for(var/obj/machinery/light/L in machines) + if(L.z != z || get_dist(src,L) > 10) + continue + + if(prob(destroy_lights)) + spawn(rand(5,25)) + L.broken() + else + L.flicker(10) + */ + + //Shifting out + else + shifted_out = TRUE + shift_state = AB_SHIFT_PASSIVE + custom_emote(1,"phases out!") + real_name = name + name = "Something" + health = maxHealth //Fullheal + + for(var/belly in vore_organs) + var/obj/belly/B = belly + B.escapable = FALSE + + overlays.Cut() + flick("phaseout",src) + sleep(30) + invisibility = INVISIBILITY_LEVEL_TWO + see_invisible = INVISIBILITY_LEVEL_TWO + update_icon() + alpha = 127 + + is_shifting = FALSE + canmove = original_canmove + incorporeal_move = TRUE + density = FALSE + force_max_speed = TRUE + +/mob/living/simple_mob/vore/demon/verb/phase_shift() + set name = "Phase Shift" + set desc = "Shift out of reality temporarily" + set category = "Abilities" + + + var/turf/T = get_turf(src) + + if(shift_state && shift_state == AB_SHIFT_PASSIVE) + to_chat(src,"You can't do a shift while passively shifting!") + return FALSE + + if(shifted_out) + to_chat(src,"You can't return to the physical world yet!") + return FALSE + + if(world.time - last_shift < 600) + to_chat(src,"You can't temporarily shift so soon! You need to wait [round(((last_shift+600)-world.time)/10)] second\s!") + return FALSE + + shift_state = AB_SHIFT_ACTIVE + forceMove(T) + var/original_canmove = canmove + SetStunned(0) + SetWeakened(0) + if(buckled) + buckled.unbuckle_mob() + if(pulledby) + pulledby.stop_pulling() + stop_pulling() + canmove = FALSE + is_shifting = TRUE + + shifted_out = TRUE + custom_emote(1,"phases out!") + real_name = name + name = "Something" + + for(var/belly in vore_organs) + var/obj/belly/B = belly + B.escapable = FALSE + + overlays.Cut() + flick("phaseout",src) + sleep(30) + invisibility = INVISIBILITY_LEVEL_TWO + see_invisible = INVISIBILITY_LEVEL_TWO + update_icon() + alpha = 127 + + is_shifting = FALSE + canmove = original_canmove + incorporeal_move = TRUE + density = FALSE + force_max_speed = TRUE + + spawn(300) + shifted_out = FALSE + name = real_name + for(var/belly in vore_organs) + var/obj/belly/B = belly + B.escapable = initial(B.escapable) + + overlays.Cut() + alpha = initial(alpha) + invisibility = initial(invisibility) + see_invisible = initial(see_invisible) + incorporeal_move = initial(incorporeal_move) + density = initial(density) + force_max_speed = initial(force_max_speed) + original_canmove = canmove + canmove = FALSE + is_shifting = TRUE + + //Cosmetics mostly + flick("phasein",src) + custom_emote(1,"phases in!") + sleep(30) //The duration of the TP animation + is_shifting = FALSE + canmove = original_canmove + + var/turf/NT = get_turf(src) + + if(!NT.CanPass(src,NT)) + for(var/direction in list(1,2,4,8,5,6,9,10)) + var/turf/L = get_step(NT, direction) + if(L) + if(L.CanPass(src,L)) + forceMove(L) + break + + //Potential phase-in vore + if(can_be_drop_pred) //Toggleable in vore panel + var/list/potentials = living_mobs(0) + if(potentials.len) + var/mob/living/target = pick(potentials) + if(istype(target) && vore_selected) + target.forceMove(vore_selected) + to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + + // Do this after the potential vore, so we get the belly + update_icon() + + shift_state = AB_SHIFT_NONE + last_shift = world.time \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm new file mode 100644 index 0000000000..acd7cafd5e --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm @@ -0,0 +1,19 @@ +/mob/living/simple_mob/vore/demon/engorge + name = "Engorge" + + icon_state = "engorge" + icon_living = "engorge" + icon_dead = "engorge_dead" + icon_rest = "engorge_rest" + + vore_icons = null + +/mob/living/simple_mob/vore/demon/zellic + name = "Zellic" + + icon_state = "zellic" + icon_living = "zellic" + icon_dead = "zellic_dead" + icon_rest = null + + vore_icons = null \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/~defines.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/~defines.dm new file mode 100644 index 0000000000..8aad13c3e3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/~defines.dm @@ -0,0 +1,3 @@ +#undef AB_SHIFT_NONE +#undef AB_SHIFT_PASSIVE +#undef AB_SHIFT_ACTIVE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm index ff54ccca27..cae232e051 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm @@ -34,6 +34,7 @@ max_co2 = 0 min_n2 = 0 max_n2 = 0 + minbodytemp = 0 // Activate Noms! /mob/living/simple_mob/vore/aggressive/dino diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm index 8e035f5b81..9f201380cf 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm @@ -24,6 +24,7 @@ min_n2 = 0 max_n2 = 0 minbodytemp = 0 + maxbodytemp = 700 old_x = -16 old_y = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm index 215697a977..605e445853 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm @@ -1,3 +1,11 @@ +/datum/category_item/catalogue/fauna/fennec //TODO: VIRGO_LORE_WRITING_WIP + name = "Wildlife - Fennec" + desc = "A small, dusty, big-eared sandfox, native to Virgo 4. It looks like a Zorren that's on all fours, \ + and it's easy to see the resemblance to the little dunefox-like critters the Zorren are. However, the fennecs \ + lack the sentience the Zorren have, and are therefore naught more than cute little critters, with a hungry \ + attitude, willing to eat damn near anything they come across or can bump into. Bapping them will make them stop." + value = CATALOGUER_REWARD_TRIVIAL + /mob/living/simple_mob/vore/fennec name = "fennec" //why isn't this in the fox file, fennecs are foxes silly. desc = "It's a dusty big-eared sandfox! Adorable!" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm new file mode 100644 index 0000000000..717d00cca9 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm @@ -0,0 +1,131 @@ +/datum/category_item/catalogue/fauna/mimic //TODO: VIRGO_LORE_WRITING_WIP + name = "Aberration - Mimic" + desc = "A being that seems to take the form of a crate, for whatever reason. \ + It seems to lie in wait for it's prey, and then pounce once the unsuspecting person attempts to open it. \ + For whatever reason, they seem native to underground areas, and they're very tough, and hard to kill, able to pounce fast." + value = CATALOGUER_REWARD_HARD + +/obj/structure/closet/crate/mimic + name = "old crate" + desc = "A rectangular steel crate. This one looks particularly unstable." + var/mimic_chance = 30 + var/mimic_active = TRUE + +/obj/structure/closet/crate/mimic/open() + if(src.opened) + return 0 + if(!src.can_open()) + return 0 + + if(mimic_active) + mimic_active = FALSE + if(prob(mimic_chance)) + var/mob/living/simple_mob/vore/aggressive/mimic/new_mimic = new(loc, src) + visible_message("The [new_mimic] suddenly growls as it turns out to be a mimic!") + forceMove(new_mimic) + new_mimic.real_crate = src + new_mimic.name = name + new_mimic.desc = desc + new_mimic.icon = icon + new_mimic.icon_state = icon_opened + new_mimic.icon_living = icon_opened + else + return ..() + else + return ..() + +/obj/structure/closet/crate/mimic/ex_act(severity) + for(var/obj/O in src.contents) + qdel(O) + qdel(src) + return + +/obj/structure/closet/crate/mimic/damage(var/damage) + if(contents.len) + visible_message("The [src] makes out a crunchy noise as its contents are destroyed!") + for(var/obj/O in src.contents) + qdel(O) + ..() + +/obj/structure/closet/crate/mimic/safe + mimic_chance = 0 + mimic_active = FALSE + +/obj/structure/closet/crate/mimic/guaranteed + mimic_chance = 100 + +/obj/structure/closet/crate/mimic/dangerous + mimic_chance = 70 + +/obj/structure/closet/crate/mimic/cointoss + mimic_chance = 50 + +/mob/living/simple_mob/vore/aggressive/mimic + name = "crate" + desc = "A rectangular steel crate." + icon_state = "crate" + icon_living = "crate" + icon = 'icons/obj/storage_vr.dmi' + + faction = "mimic" + + maxHealth = 125 + health = 125 + movement_cooldown = 7 + + response_help = "touches" + response_disarm = "pushes" + response_harm = "hits" + + melee_damage_lower = 7 + melee_damage_upper = 15 + attacktext = list("attacked") + attack_sound = 'sound/weapons/bite.ogg' + + 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 + + ai_holder_type = /datum/ai_holder/mimic + + var/obj/structure/closet/crate/real_crate + + var/knockdown_chance = 10 //Stubbing your toe on furniture hurts. + + showvoreprefs = 0 //Hides mechanical vore prefs for mimics. You can't see their gaping maws when they're just sitting idle. + +/mob/living/simple_mob/vore/aggressive/mimic + vore_active = 1 + vore_pounce_chance = 10 + swallowTime = 3 SECONDS + vore_capacity = 1 + vore_default_mode = DM_DIGEST + +/datum/ai_holder/mimic + wander = FALSE + hostile = TRUE + +/mob/living/simple_mob/vore/aggressive/mimic/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(knockdown_chance)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) + +/mob/living/simple_mob/vore/aggressive/mimic/will_show_tooltip() + return FALSE + +/mob/living/simple_mob/vore/aggressive/mimic/death() + ..() + if(real_crate) + real_crate.forceMove(loc) + else + new/obj/structure/closet/crate(loc) + real_crate = null + qdel(src) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm new file mode 100644 index 0000000000..5efd855450 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -0,0 +1,182 @@ +#define MORPH_COOLDOWN 50 + +/mob/living/simple_mob/vore/hostile/morph + name = "morph" + real_name = "morph" + desc = "A revolting, pulsating pile of flesh." + tt_desc = "morphus shapeshiftus" + icon = 'icons/mob/animal_vr.dmi' + icon_state = "morph" + icon_living = "morph" + icon_dead = "morph_dead" + movement_cooldown = 1 + status_flags = CANPUSH + pass_flags = PASSTABLE + mob_bump_flag = SLIME + + 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 + maxHealth = 250 + health = 250 + taser_kill = FALSE + melee_damage_lower = 15 + melee_damage_upper = 20 + see_in_dark = 8 + + response_help = "touches" + response_disarm = "pushes" + response_harm = "hits" + attacktext = "glomps" + attack_sound = 'sound/effects/blobattack.ogg' + + meat_amount = 2 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + showvoreprefs = 0 + vore_active = 1 + + var/morphed = FALSE + var/tooltip = TRUE + var/melee_damage_disguised = 0 + var/eat_while_disguised = FALSE + var/atom/movable/form = null + var/morph_time = 0 + var/our_size_multiplier = 1 + var/static/list/blacklist_typecache = typecacheof(list( + /obj/screen, + /obj/singularity, + /mob/living/simple_mob/vore/hostile/morph, + /obj/effect)) + +/mob/living/simple_mob/vore/hostile/morph/Initialize() + verbs += /mob/living/proc/ventcrawl + return ..() + +/mob/living/simple_mob/vore/hostile/morph/proc/allowed(atom/movable/A) + return !is_type_in_typecache(A, blacklist_typecache) && (isobj(A) || ismob(A)) + +/mob/living/simple_mob/vore/hostile/morph/examine(mob/user) + if(morphed) + form.examine(user) + if(get_dist(user,src)<=3) + to_chat(user, "It doesn't look quite right...") + else + ..() + return + +/mob/living/simple_mob/vore/hostile/morph/ShiftClickOn(atom/movable/A) + if(Adjacent(A)) + if(morph_time <= world.time && !stat) + if(A == src) + restore() + return + if(istype(A) && allowed(A)) + assume(A) + else + to_chat(src, "Your chameleon skin is still repairing itself!") + else + ..() + +/mob/living/simple_mob/vore/hostile/morph/proc/assume(atom/movable/target) + if(morphed) + to_chat(src, "You must restore to your original form first!") + return + morphed = TRUE + form = target + + visible_message("[src] suddenly twists and changes shape, becoming a copy of [target]!") + appearance = target.appearance + copy_overlays(target) + alpha = max(alpha, 150) //fucking chameleons + transform = initial(transform) + our_size_multiplier = size_multiplier + if(isobj(target)) + size_multiplier = 1 + icon_scale_x = target.icon_scale_x + icon_scale_y = target.icon_scale_y + update_transform() + else if(ismob(target)) + var/mob/living/M = target + resize(M.size_multiplier) + pixel_y = initial(pixel_y) + pixel_x = initial(pixel_x) + density = target.density + + //Morphed is weaker + melee_damage_lower = melee_damage_disguised + melee_damage_upper = melee_damage_disguised + movement_cooldown = 5 + + morph_time = world.time + MORPH_COOLDOWN + return + +/mob/living/simple_mob/vore/hostile/morph/proc/restore() + if(!morphed) + to_chat(src, "You're already in your normal form!") + return + morphed = FALSE + form = null + alpha = initial(alpha) + color = initial(color) + layer = initial(layer) + plane = initial(plane) + maptext = null + + visible_message("[src] suddenly collapses in on itself, dissolving into a pile of green flesh!") + name = initial(name) + desc = initial(desc) + icon = initial(icon) + icon_state = initial(icon_state) + size_multiplier = 0 + resize(our_size_multiplier) + overlays.Cut() + density = initial(density) + + //Baseline stats + melee_damage_lower = initial(melee_damage_lower) + melee_damage_upper = initial(melee_damage_upper) + movement_cooldown = initial(movement_cooldown) + + morph_time = world.time + MORPH_COOLDOWN + +/mob/living/simple_mob/vore/hostile/morph/death(gibbed) + if(morphed) + visible_message("[src] twists and dissolves into a pile of green flesh!") + restore() + ..() + +/mob/living/simple_mob/vore/hostile/morph/will_show_tooltip() + return (!morphed) + +/mob/living/simple_mob/vore/hostile/morph/resize(var/new_size, var/animate = TRUE) + if(morphed && !ismob(form)) + return + return ..() + +/mob/living/simple_mob/vore/hostile/morph/update_icon() + if(morphed) + return + return ..() + + +/mob/living/simple_mob/vore/hostile/morph/update_icons() + if(morphed) + return + return ..() + +/mob/living/simple_mob/vore/hostile/morph/update_transform() + if(morphed) + var/matrix/M = matrix() + M.Scale(icon_scale_x, icon_scale_y) + M.Turn(icon_rotation) + src.transform = M + else + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm index 44b9ff59e5..07eb1a38e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm @@ -1,7 +1,10 @@ -// ToDo: Make this code not a fucking snowflaky horrible broken mess. Do not use until it's actually fixed. It's miserably bad right now. -// Also ToDo: Dev-to-dev communication to ensure responsible parties (if available. In this case, yes.) are aware of what's going on and what's broken. -// Probably easier to troubleshoot when we ain't breaking the server by spawning a buttload of heavily extra feature coded snowflake mobs to the wilderness as mass cannonfodder. -// Also ToDo: An actual "simple" mob for that purpose if necessary :v +/datum/category_item/catalogue/fauna/otie //TODO: VIRGO_LORE_WRITING_WIP + name = "Creature - Otie" + desc = "A bioengineered longdog, the otie is very long, and very cute, depending on if you like dogs, \ + especially long ones. They are black-and-grey furred, typically, and tanky, hard to kill. \ + They seem hostile at first, but are also tame-able if you can approach one. Nipnipnip-ACK \ + **the catalogue entry ends here.**" + value = CATALOGUER_REWARD_MEDIUM /mob/living/simple_mob/otie //Spawn this one only if you're looking for a bad time. Not friendly. name = "otie" @@ -13,25 +16,9 @@ icon_dead = "otie-dead" icon_rest = "otie_rest" faction = "otie" - recruitable = 1 maxHealth = 150 health = 150 minbodytemp = 200 - move_to_delay = 4 - hostile = 1 - investigates = 1 - reacts = 1 - animal = 1 - specific_targets = 1 - run_at_them = 0 - attack_same = 0 - speak_chance = 4 - speak = list("Boof.","Waaf!","Prurr.","Bork!","Rurrr..","Arf.") - speak_emote = list("growls", "roars", "yaps", "Awoos") - emote_hear = list("rurrs", "rumbles", "rowls", "groans softly", "murrs", "sounds hungry", "yawns") - emote_see = list("stares ferociously", "snarls", "licks their chops", "stretches", "yawns") - say_maybe_target = list("Ruh?", "Waf?") - say_got_target = list("Rurrr!", "ROAR!", "MARR!", "RERR!", "RAHH!", "RAH!", "WARF!") melee_damage_lower = 2 melee_damage_upper = 7 //Don't break my bones bro response_help = "pets the" @@ -52,9 +39,9 @@ buckle_lying = FALSE mount_offset_y = 10 - var/glowyeyes = FALSE - var/image/eye_layer = null - var/eyetype + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive/otie + say_list_type = /datum/say_list/otie + var/mob/living/carbon/human/friend var/tamed = 0 var/tame_chance = 50 //It's a fiddy-fiddy default you may get a buddy pal or you may get mauled and ate. Win-win! @@ -71,14 +58,12 @@ name = "mutated feral otie" desc = "The classic bioengineered longdog. No pets. Only bite. This one has mutated from too much time out on the surface of Virgo-3B." tt_desc = "Otus phoronis" - icon_state = "siftusian" - icon_living = "siftusian" - icon_dead = "siftusian-dead" - icon_rest = "siftusian_rest" + icon_state = "photie" + icon_living = "photie" + icon_dead = "photie-dead" + icon_rest = "photie_rest" faction = "virgo3b" tame_chance = 5 // Only a 1 in 20 chance of success. It's feral. What do you expect? - melee_damage_lower = 5 - melee_damage_upper = 12 // Lazy way of making sure this otie survives outside. min_oxy = 0 max_oxy = 0 @@ -88,8 +73,7 @@ max_co2 = 0 min_n2 = 0 max_n2 = 0 - glowyeyes = TRUE - eyetype = "photie" + has_eye_glow = TRUE /mob/living/simple_mob/otie/red name = "feral red otie" @@ -101,8 +85,6 @@ icon_rest = "hotie_rest" faction = "cult" tame_chance = 20 - melee_damage_lower = 5 - melee_damage_upper = 12 // Lazy way of making sure this otie survives outside. min_oxy = 0 max_oxy = 0 @@ -112,8 +94,8 @@ max_co2 = 0 min_n2 = 0 max_n2 = 0 - glowyeyes = TRUE - eyetype = "hotie" + maxbodytemp = 1000 + has_eye_glow = TRUE /mob/living/simple_mob/otie/red/friendly //gets the pet2tame feature and doesn't kill you right away name = "red otie" @@ -143,7 +125,7 @@ icon_state = "pcotie" icon_living = "pcotie" icon_rest = "pcotie_rest" - icon_dead = "siftusian-dead" + icon_dead = "photie-dead" min_oxy = 0 max_oxy = 0 min_tox = 0 @@ -152,8 +134,7 @@ max_co2 = 0 min_n2 = 0 max_n2 = 0 - glowyeyes = TRUE - eyetype = "photie" + has_eye_glow = TRUE /mob/living/simple_mob/otie/security //tame by default unless you're a marked crimester. can be befriended to follow with pets tho. name = "guard otie" @@ -166,8 +147,7 @@ maxHealth = 200 //armored or something health = 200 tamed = 1 - glowyeyes = TRUE - eyetype = "sotie" + has_eye_glow = TRUE loot_list = list(/obj/item/clothing/glasses/sunglasses/sechud,/obj/item/clothing/suit/armor/vest/alt) vore_pounce_chance = 60 // Good boys don't do too much police brutality. @@ -176,14 +156,12 @@ /mob/living/simple_mob/otie/security/phoron name = "mutated guard otie" - desc = "An extra rare phoron resistant version of the VARMAcorp trained snowflake guard dogs." + desc = "An extra rare phoron resistant version of the VARMAcorp trained snowflake guard dogs for infernal environments." tt_desc = "Otus phoronis" - icon_state = "sifguard" - icon_living = "sifguard" - icon_rest = "sifguard_rest" - icon_dead = "sifguard-dead" - melee_damage_lower = 5 - melee_damage_upper = 12 + icon_state = "secphotie" + icon_living = "secphotie" + icon_rest = "secphotie_rest" + icon_dead = "secphotie-dead" min_oxy = 0 max_oxy = 0 min_tox = 0 @@ -192,48 +170,22 @@ max_co2 = 0 min_n2 = 0 max_n2 = 0 - glowyeyes = TRUE - eyetype = "sotie" -/mob/living/simple_mob/otie/PunchTarget() - if(istype(target_mob,/mob/living/simple_mob/animal/passive/mouse)) - return EatTarget() - else ..() - -/mob/living/simple_mob/otie/Found(var/atom/found_atom) - if(!SA_attackable(found_atom)) - return null - if(istype(found_atom,/mob/living/simple_mob/animal/passive/mouse)) - return found_atom - else if(ismob(found_atom)) - var/mob/found_mob = found_atom - if(found_mob.faction == faction) - return null - else if(friend == found_atom) - return null - else if(tamed == 1 && ishuman(found_atom)) - return null - else if(tamed == 1 && isrobot(found_atom)) - return null - else - if(resting) - lay_down() - return found_atom - else - return null - -/mob/living/simple_mob/otie/security/Found(var/atom/found_atom) - if(check_threat(found_atom) >= 4) - if(resting) - lay_down() - return found_atom - ..() +/mob/living/simple_mob/otie/security/phoron/red + name = "red guard otie" + desc = "An ominous looking version of the VARMAcorp trained snowflake guard dogs." + tt_desc = "Otus infernalis" + icon_state = "sechotie" + icon_living = "sechotie" + icon_rest = "sechotie_rest" + icon_dead = "sechotie-dead" + maxbodytemp = 1000 /mob/living/simple_mob/otie/attackby(var/obj/item/O, var/mob/user) // Trade donuts for bellybrig victims. if(istype(O, /obj/item/weapon/reagent_containers/food)) qdel(O) playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1) - if(ai_inactive)//No autobarf on player control. + if(!has_AI())//No autobarf on player control. return if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/donut) && istype(src, /mob/living/simple_mob/otie/security)) to_chat(user,"The guard pup accepts your offer for their catch.") @@ -258,80 +210,12 @@ return 0 return M.assess_perp(0, 0, 0, check_records, check_arrest) -/mob/living/simple_mob/otie/security/set_target(var/mob/M) - ai_log("SetTarget([M])",2) - if(!M || (world.time - last_target_time < 5 SECONDS) && target_mob) - ai_log("SetTarget() can't set it again so soon",3) - return 0 - - var/turf/seen = get_turf(M) - - if(investigates && (annoyed < 10)) - try_say_list(say_maybe_target) - face_atom(seen) - annoyed += 14 - sleep(1 SECOND) //For realism - - if(M in ListTargets(view_range)) - try_say_list(say_got_target) - target_mob = M - last_target_time = world.time - if(check_threat(M) >= 4) - global_announcer.autosay("[src] is attempting to detain suspect [target_name(M)] in [get_area(src)].", "SmartCollar oversight", "Security") - return M - else if(investigates) - spawn(1) - WanderTowards(seen) - - return 0 - - /mob/living/simple_mob/otie/security/proc/target_name(mob/living/T) if(ishuman(T)) var/mob/living/carbon/human/H = T return H.get_id_name("unidentified person") return "unidentified lifeform" -//Basic friend AI - -/mob/living/simple_mob/otie/Life() - . = ..() - if(!. || ai_inactive) return - - if(prob(5) && (stance == STANCE_IDLE)) - lay_down() - - if(!friend) return - - var/friend_dist = get_dist(src,friend) - - if (friend_dist <= 4) - if(stance == STANCE_IDLE) - if(set_follow(friend)) - handle_stance(STANCE_FOLLOW) - if(resting) - lay_down() - - if (friend_dist <= 1) - if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) - if (prob((friend.stat < DEAD)? 50 : 15)) - var/verb = pick("whines", "yelps", "whimpers") - audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) - else - if (prob(5)) - visible_emote(pick("nuzzles [friend].", - "brushes against [friend].", - "rubs against [friend].", - "noses at [friend].", - "slobberlicks [friend].", - "murrs contently.", - "leans on [friend].", - "nibbles affectionately on [friend].")) - else if (friend.health <= 50) - if (prob(10)) - var/verb = pick("whines", "yelps", "whimpers") - audible_emote("[verb] anxiously.") - //Pet 4 friendly /mob/living/simple_mob/otie/attack_hand(mob/living/carbon/human/M as mob) @@ -340,24 +224,26 @@ if(I_HELP) if(health > 0) M.visible_message("[M] [response_help] \the [src].") - if(ai_inactive) - return - LoseTarget() - handle_stance(STANCE_IDLE) - if(prob(tame_chance)) - friend = M - if(tamed != 1) - tamed = 1 - faction = M.faction - sleep(1 SECOND) + if(has_AI()) + var/datum/ai_holder/AI = ai_holder + AI.set_stance(STANCE_IDLE) + if(prob(tame_chance)) + AI.hostile = FALSE + friend = M + AI.set_follow(friend) + if(tamed != 1) + tamed = 1 + faction = M.faction + sleep(1 SECOND) if(I_GRAB) if(health > 0) - if(ai_inactive) - return - audible_emote("growls disapprovingly at [M].") - if(M == friend) - friend = null + if(has_AI()) + var/datum/ai_holder/AI = ai_holder + audible_emote("growls disapprovingly at [M].") + if(M == friend) + AI.lose_follow() + friend = null return else ..() @@ -365,36 +251,30 @@ else ..() -/mob/living/simple_mob/otie/proc/add_eyes() - if(!eye_layer) - eye_layer = image(icon, "[eyetype]-eyes") - eye_layer.plane = PLANE_LIGHTING_ABOVE - add_overlay(eye_layer) - -/mob/living/simple_mob/otie/proc/remove_eyes() - cut_overlay(eye_layer) - -/mob/living/simple_mob/otie/New() - if(glowyeyes) - add_eyes() - ..() - -/mob/living/simple_mob/otie/update_icon() - . = ..() - remove_eyes() - if(glowyeyes && stat == CONSCIOUS && !resting) - add_eyes() - /mob/living/simple_mob/otie/death(gibbed, deathmessage = "dies!") .=..() resting = 0 icon_state = icon_dead -/mob/living/simple_animal/otie/Login() +/mob/living/simple_mob/otie/Login() . = ..() if(!riding_datum) - riding_datum = new /datum/riding/simple_animal(src) - verbs |= /mob/living/simple_animal/proc/animal_mount + riding_datum = new /datum/riding/simple_mob(src) + verbs |= /mob/living/simple_mob/proc/animal_mount -/mob/living/simple_animal/otie/MouseDrop_T(mob/living/M, mob/living/user) +/mob/living/simple_mob/otie/MouseDrop_T(mob/living/M, mob/living/user) return + +/datum/say_list/otie + speak = list("Boof.","Waaf!","Prurr.","Bork!","Rurrr..","Arf.") + emote_hear = list("rurrs", "rumbles", "rowls", "groans softly", "murrs", "yawns") + emote_see = list("stares ferociously", "snarls", "licks their chops", "stretches", "yawns") + say_maybe_target = list("Ruh?", "Waf?") + say_got_target = list("Rurrr!", "ROAR!", "MARR!", "RERR!", "RAHH!", "RAH!", "WARF!") + +/datum/ai_holder/simple_mob/melee/evasive/otie + +/datum/ai_holder/simple_mob/melee/evasive/otie/New(var/mob/living/simple_mob/otie/new_holder) + .=..() + if(new_holder.tamed) + hostile = FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm index b4fed334bc..f7881a15c1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm @@ -1,3 +1,10 @@ +/datum/category_item/catalogue/fauna/rat //TODO: VIRGO_LORE_WRITING_WIP + name = "Creature - Rat" + desc = "A massive rat, some sort of mutated descendant of normal Earth rats. These ones seem particularly hungry, \ + and are able to pounce and stun their targets - presumably to eat them. Their bodies are long and greyfurred, \ + with a pink nose and large teeth, just like their regular-sized counterparts." + value = CATALOGUER_REWARD_MEDIUM + /mob/living/simple_mob/vore/aggressive/rat name = "giant rat" desc = "In what passes for a hierarchy among verminous rodents, this one is king." @@ -42,7 +49,7 @@ var/life_since_foodscan = 0 - say_list_type = /datum/say_list/ratte + say_list_type = /datum/say_list/rat ai_holder_type = /datum/ai_holder/simple_mob/melee/rat /mob/living/simple_mob/vore/aggressive/rat/tame //not quite tame but does not attack on sight @@ -167,12 +174,38 @@ if(!riding_datum) riding_datum = new /datum/riding/simple_animal(src) verbs |= /mob/living/simple_animal/proc/animal_mount -*/ /mob/living/simple_mob/vore/aggressive/rat/MouseDrop_T(mob/living/M, mob/living/user) return +*/ -/datum/say_list/ratte +/mob/living/simple_mob/vore/aggressive/rat/phoron + name = "phoron rat" + desc = "In what passes for a hierarchy among verminous rodents, this one is alien overlord." + tt_desc = "Mus muscular phoronis" + + icon_state = "phorous" //TODO: proper phoron rat sprites + icon_living = "phorous" + icon_dead = "phorous-dead" + icon_rest = "phorous_rest" + + maxHealth = 175 + health = 175 + + melee_damage_lower = 8 + melee_damage_upper = 16 + + 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 + +/datum/say_list/rat speak = list("Squeek!","SQUEEK!","Squeek?") emote_hear = list("squeeks","squeaks","squiks") emote_see = list("runs in a circle", "shakes", "scritches at something") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm index b702e51f2e..fcf62fe85a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm @@ -8,7 +8,7 @@ faction = "shadekin" ui_icons = 'icons/mob/shadekin_hud.dmi' mob_class = MOB_CLASS_HUMANOID - mob_bump_flag = 0 + mob_bump_flag = HUMAN maxHealth = 200 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm index e9ef05cbec..e66162432b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm @@ -92,6 +92,16 @@ /mob/living/simple_mob/shadekin/blue/brown icon_state = "brown" +/mob/living/simple_mob/shadekin/blue/ai + ai_holder_type = /datum/ai_holder/simple_mob/passive + +/mob/living/simple_mob/shadekin/blue/ai/white + icon_state = "white" +/mob/living/simple_mob/shadekin/blue/ai/dark + icon_state = "dark" +/mob/living/simple_mob/shadekin/blue/ai/brown + icon_state = "brown" + ///////////////////////////////////////////////////////////////// /mob/living/simple_mob/shadekin/purple name = "purple-eyed shadekin" @@ -134,6 +144,16 @@ /mob/living/simple_mob/shadekin/purple/brown icon_state = "brown" +/mob/living/simple_mob/shadekin/purple/ai + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +/mob/living/simple_mob/shadekin/purple/ai/white + icon_state = "white" +/mob/living/simple_mob/shadekin/purple/ai/dark + icon_state = "dark" +/mob/living/simple_mob/shadekin/purple/ai/brown + icon_state = "brown" + ///////////////////////////////////////////////////////////////// /mob/living/simple_mob/shadekin/yellow name = "yellow-eyed shadekin" @@ -174,6 +194,26 @@ /mob/living/simple_mob/shadekin/yellow/brown icon_state = "brown" +/mob/living/simple_mob/shadekin/yellow/ai + ai_holder_type = /datum/ai_holder/simple_mob/melee/hit_and_run + +/mob/living/simple_mob/shadekin/yellow/ai/white + icon_state = "white" +/mob/living/simple_mob/shadekin/yellow/ai/dark + icon_state = "dark" +/mob/living/simple_mob/shadekin/yellow/ai/brown + icon_state = "brown" + +/mob/living/simple_mob/shadekin/yellow/ai/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +/mob/living/simple_mob/shadekin/yellow/ai/retaliate/white + icon_state = "white" +/mob/living/simple_mob/shadekin/yellow/ai/retaliate/dark + icon_state = "dark" +/mob/living/simple_mob/shadekin/yellow/ai/retaliate/brown + icon_state = "brown" + ///////////////////////////////////////////////////////////////// /mob/living/simple_mob/shadekin/green name = "green-eyed shadekin" @@ -214,6 +254,16 @@ /mob/living/simple_mob/shadekin/green/brown icon_state = "brown" +/mob/living/simple_mob/shadekin/green/ai + ai_holder_type = /datum/ai_holder/simple_mob/passive + +/mob/living/simple_mob/shadekin/green/ai/white + icon_state = "white" +/mob/living/simple_mob/shadekin/green/ai/dark + icon_state = "dark" +/mob/living/simple_mob/shadekin/green/ai/brown + icon_state = "brown" + ///////////////////////////////////////////////////////////////// /mob/living/simple_mob/shadekin/orange name = "orange-eyed shadekin" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm index dcb6499121..7509d81af9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm @@ -8,11 +8,19 @@ List of things solar grubs should be able to do: 6. add glow? */ +/datum/category_item/catalogue/fauna/solargrub //TODO: VIRGO_LORE_WRITING_WIP + name = "Solargrub" + desc = "Some form of mutated space larva, they seem to crop up on space stations wherever there is power. \ + They seem to have the chance to cocoon and mutate if left alone, but no recorded instances of this have happened yet. \ + Therefore, if you see the grubs, kill them while they're small, or things might escalate." // TODO: PORT SOLAR MOTHS - Rykka + value = CATALOGUER_REWARD_EASY + #define SINK_POWER 1 -/mob/living/simple_mob/animal/space/solargrub +/mob/living/simple_mob/vore/solargrub name = "juvenile solargrub" desc = "A young sparkling solargrub" + catalogue_data = list(/datum/category_item/catalogue/fauna/solargrub) icon = 'icons/mob/vore.dmi' //all of these are placeholders icon_state = "solargrub" icon_living = "solargrub" @@ -23,9 +31,9 @@ List of things solar grubs should be able to do: health = 50 melee_damage_lower = 1 - melee_damage_upper = 3 + melee_damage_upper = 3 //low damage, but poison and stuns are strong - speed = 2 + movement_cooldown = 8 meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/grubmeat @@ -33,31 +41,29 @@ List of things solar grubs should be able to do: response_disarm = "pushes" response_harm = "roughly pushes" + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/solargrub + say_list_type = /datum/say_list/solargrub + var/poison_per_bite = 5 //grubs cause a shock when they bite someone var/poison_type = "shockchem" var/poison_chance = 50 var/datum/powernet/PN // Our powernet var/obj/structure/cable/attached // the attached cable - var/emp_chance = 20 // Beware synths + var/shock_chance = 10 // Beware /datum/say_list/solargrub emote_see = list("squelches", "squishes") -/* //Commented out pending reworks - 2/2/19 -/mob/living/simple_mob/animal/solargrub/PunchTarget() - if(target_mob&& prob(emp_chance)) - target_mob.emp_act(4) //The weakest strength of EMP - visible_message("The grub releases a powerful shock!") - ..() -/mob/living/simple_mob/animal/solargrub/Life() +/mob/living/simple_mob/vore/solargrub/Life() . = ..() - if(!. || ai_inactive) return + if(!.) return - if(stance == STANCE_IDLE) + if(!ai_holder.target) //first, check for potential cables nearby to powersink var/turf/S = loc - attached = locate() in S + attached = locate(/obj/structure/cable) in S if(attached) + set_AI_busy(TRUE) if(prob(2)) src.visible_message("\The [src] begins to sink power from the net.") if(prob(5)) @@ -78,31 +84,52 @@ List of things solar grubs should be able to do: else if(!attached && anchored) anchored = 0 PN = null -*/ -/mob/living/simple_mob/animal/solargrub //active noms + +/mob/living/simple_mob/vore/solargrub //active noms vore_bump_chance = 50 vore_bump_emote = "applies minimal effort to try and slurp up" vore_active = 1 vore_capacity = 1 vore_pounce_chance = 0 //grubs only eat incapacitated targets vore_default_mode = DM_DIGEST -/* -/mob/living/simple_mob/animal/solargrub/PunchTarget() - . = ..() - if(isliving(.)) - var/mob/living/L = . - if(L.reagents) - if(prob(poison_chance)) - L << "You feel a shock rushing through your veins." - L.reagents.add_reagent(poison_type, poison_per_bite) */ -/mob/living/simple_mob/animal/solargrub/death() +/mob/living/simple_mob/vore/solargrub/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(shock_chance)) + A.emp_act(4) //The weakest strength of EMP + playsound(src, 'sound/weapons/Egloves.ogg', 75, 1) + L.Weaken(4) + L.Stun(4) + L.stuttering = max(L.stuttering, 4) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, L) + s.start() + visible_message("The grub releases a powerful shock!") + else + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/vore/solargrub/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a small shock rushing through your veins.") + L.reagents.add_reagent(poison_type, poison_per_bite) + +/mob/living/simple_mob/vore/solargrub/death() src.anchored = 0 set_light(0) ..() -/mob/living/simple_mob/animal/solargrub/handle_light() +/mob/living/simple_mob/vore/solargrub/handle_light() . = ..() if(. == 0 && !is_dead()) set_light(2.5, 1, COLOR_YELLOW) return 1 + +/datum/ai_holder/simple_mob/retaliate/solargrub/react_to_attack(atom/movable/attacker) + holder.anchored = 0 + holder.set_AI_busy(FALSE) + ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm index 898a3d2757..b06f5ed4f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm @@ -1,8 +1,9 @@ var/global/list/grub_machine_overlays = list() -/mob/living/simple_mob/solargrub_larva +/mob/living/simple_mob/animal/solargrub_larva name = "solargrub larva" desc = "A tiny wormy thing that can grow to massive sizes under the right conditions." + catalogue_data = list(/datum/category_item/catalogue/fauna/solargrub) icon = 'icons/mob/vore.dmi' icon_state = "grublarva" icon_living = "grublarva" @@ -10,6 +11,10 @@ var/global/list/grub_machine_overlays = list() health = 5 maxHealth = 5 + movement_cooldown = 3 + + melee_damage_lower = 1 // This is a tiny worm. It will nibble and thats about it. + melee_damage_upper = 1 meat_amount = 2 meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/grubmeat @@ -20,30 +25,30 @@ var/global/list/grub_machine_overlays = list() response_disarm = "nudges" response_harm = "stomps on" + mob_size = MOB_MINISCULE pass_flags = PASSTABLE can_pull_size = ITEMSIZE_TINY can_pull_mobs = MOB_PULL_NONE density = 0 - stop_when_pulled = 0 + //stop_when_pulled = 0 var/static/list/ignored_machine_types = list( - /obj/machinery/atmospherics/unary/vent_pump, /obj/machinery/atmospherics/unary/vent_scrubber, - /obj/machinery/door/firedoor + /obj/machinery/door/firedoor, + /obj/machinery/button/windowtint ) - var/obj/machinery/atmospherics/unary/vent_pump/target_vent - var/datum/effect/effect/system/spark_spread/sparks var/image/machine_effect var/obj/machinery/abstract_grub_machine/powermachine var/power_drained = 0 - var/forced_out = 0 -/mob/living/simple_mob/solargrub_larva/New() + ai_holder_type = /datum/ai_holder/simple_mob/solargrub_larva + +/mob/living/simple_mob/animal/solargrub_larva/New() ..() powermachine = new(src) sparks = new(src) @@ -51,25 +56,24 @@ var/global/list/grub_machine_overlays = list() sparks.attach(src) verbs += /mob/living/proc/ventcrawl -/mob/living/simple_mob/solargrub_larva/death() +/mob/living/simple_mob/animal/solargrub_larva/death() powermachine.draining = 0 set_light(0) return ..() -/mob/living/simple_mob/solargrub_larva/Destroy() +/mob/living/simple_mob/animal/solargrub_larva/Destroy() QDEL_NULL(powermachine) QDEL_NULL(sparks) QDEL_NULL(machine_effect) - target_vent = null return ..() -/mob/living/simple_mob/solargrub_larva/Life() +/mob/living/simple_mob/animal/solargrub_larva/Life() . = ..() if(machine_effect && !istype(loc, /obj/machinery)) QDEL_NULL(machine_effect) - if(!. || ai_inactive) + if(!.) // || ai_inactive return if(power_drained >= 7 MEGAWATTS && prob(5)) @@ -84,54 +88,31 @@ var/global/list/grub_machine_overlays = list() sparks.start() return - if(stance == STANCE_IDLE) - if(forced_out) - forced_out = Clamp(0, forced_out--, forced_out) +/mob/living/simple_mob/animal/solargrub_larva/attack_target(atom/A) + if(istype(A, /obj/machinery) && !istype(A, /obj/machinery/atmospherics/unary/vent_pump)) + var/obj/machinery/M = A + if(is_type_in_list(M, ignored_machine_types)) return - - if(target_vent) - if(Adjacent(target_vent)) - spawn() - do_ventcrawl(target_vent) - target_vent = null - else - target_vent = null - stop_automated_movement = 0 - walk(src, 0) + if(!M.idle_power_usage && !M.active_power_usage && !(istype(M, /obj/machinery/power/apc) || istype(M, /obj/machinery/power/smes))) return + if(locate(/mob/living/simple_mob/animal/solargrub_larva) in M) + return + enter_machine(M) + return TRUE - if(prob(20)) - var/list/possible_machines = list() - for(var/obj/machinery/M in orange(1,src)) - if(!Adjacent(M)) - continue - if(istype(M, /obj/machinery/power/apc) || istype(M, /obj/machinery/power/smes)) //APCs and SMES units don't actually use power, but it's too thematic to ignore them - possible_machines += M - continue - if(is_type_in_list(M, ignored_machine_types)) - continue - if(!M.idle_power_usage && !M.active_power_usage) //If it can't use power at all, ignore it - continue - possible_machines += M - if(possible_machines.len) - enter_machine(pick(possible_machines)) - return + if(istype(A, /obj/machinery/atmospherics/unary/vent_pump)) + var/obj/machinery/atmospherics/unary/vent_pump/V = A + if(V.welded) + return + do_ventcrawl(V) + return TRUE - if(prob(10)) - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/vent in view(7,src)) - if(vent.welded) - continue - vents += vent - if(vents.len) - var/picked = pick(vents) - target_vent = picked - WanderTowards(get_turf(picked)) - return + return FALSE -/mob/living/simple_mob/solargrub_larva/proc/enter_machine(var/obj/machinery/M) +/mob/living/simple_mob/animal/solargrub_larva/proc/enter_machine(var/obj/machinery/M) if(!istype(M)) return + set_AI_busy(TRUE) forceMove(M) powermachine.draining = 2 visible_message("\The [src] finds an opening and crawls inside \the [M].") @@ -141,13 +122,13 @@ var/global/list/grub_machine_overlays = list() for(var/mob/L in player_list) //because nearly every machine updates its icon by removing all overlays first L << machine_effect -/mob/living/simple_mob/solargrub_larva/proc/generate_machine_effect(var/obj/machinery/M) +/mob/living/simple_mob/animal/solargrub_larva/proc/generate_machine_effect(var/obj/machinery/M) var/icon/I = new /icon(M.icon, M.icon_state) I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) I.Blend(new /icon('icons/effects/alert.dmi', "_red"),ICON_MULTIPLY) grub_machine_overlays[M.type] = I -/mob/living/simple_mob/solargrub_larva/proc/eject_from_machine(var/obj/machinery/M) +/mob/living/simple_mob/animal/solargrub_larva/proc/eject_from_machine(var/obj/machinery/M) if(!M) if(istype(loc, /obj/machinery)) M = loc @@ -157,10 +138,12 @@ var/global/list/grub_machine_overlays = list() sparks.start() if(machine_effect) QDEL_NULL(machine_effect) - forced_out += rand(5,15) + ai_holder.target = null powermachine.draining = 1 + spawn(30) + set_AI_busy(FALSE) -/mob/living/simple_mob/solargrub_larva/proc/do_ventcrawl(var/obj/machinery/atmospherics/unary/vent_pump/vent) +/mob/living/simple_mob/animal/solargrub_larva/proc/do_ventcrawl(var/obj/machinery/atmospherics/unary/vent_pump/vent) if(!vent) return var/obj/machinery/atmospherics/unary/vent_pump/end_vent = get_safe_ventcrawl_target(vent) @@ -184,15 +167,14 @@ var/global/list/grub_machine_overlays = list() playsound(end_vent, 'sound/machines/ventcrawl.ogg', 50, 1, -3) forceMove(get_turf(end_vent)) -/mob/living/simple_mob/solargrub_larva/proc/expand_grub() +/mob/living/simple_mob/animal/solargrub_larva/proc/expand_grub() eject_from_machine() visible_message("\The [src] suddenly balloons in size!") - new /mob/living/simple_mob/animal/solargrub(get_turf(src)) -// var/mob/living/simple_mob/animal/solargrub/grub = new(get_turf(src)) + new /mob/living/simple_mob/vore/solargrub(get_turf(src)) // grub.power_drained = power_drained //TODO qdel(src) -/mob/living/simple_mob/solargrub_larva/handle_light() +/mob/living/simple_mob/animal/solargrub_larva/handle_light() . = ..() if(. == 0 && !is_dead()) set_light(1.5, 1, COLOR_YELLOW) @@ -205,7 +187,61 @@ var/global/list/grub_machine_overlays = list() var/total_idle_power_usage = 3 KILOWATTS var/list/idle_power_usages = list(1 KILOWATTS, 1 KILOWATTS, 1 KILOWATTS) var/draining = 1 - var/mob/living/simple_mob/solargrub_larva/grub + var/mob/living/simple_mob/animal/solargrub_larva/grub + + +/datum/ai_holder/simple_mob/solargrub_larva + //var/fleeing + var/static/list/ignored_machine_types = list( + /obj/machinery/atmospherics/unary/vent_scrubber, + /obj/machinery/door/firedoor, + /obj/machinery/button/windowtint + ) + var/list/ignored_targets = list() + +/datum/ai_holder/simple_mob/solargrub_larva/list_targets() + var/static/potential_targets = typecacheof(list(/obj/machinery)) + var/list/actual_targets = list() + + for(var/AT in typecache_filter_list(range(vision_range, holder), potential_targets)) + var/obj/machinery/M = AT + if(istype(M, /obj/machinery/atmospherics/unary/vent_pump)) + var/obj/machinery/atmospherics/unary/vent_pump/V = M + if(!V.welded && prob(50)) + actual_targets += M + continue + if(is_type_in_list(M, ignored_machine_types)) + continue + if(!M.idle_power_usage && !M.active_power_usage && !(istype(M, /obj/machinery/power/apc) || istype(M, /obj/machinery/power/smes))) + continue + if(locate(/mob/living/simple_mob/animal/solargrub_larva) in M) + continue + if(M in ignored_targets) + continue + actual_targets += M + return actual_targets + +/datum/ai_holder/simple_mob/solargrub_larva/can_attack(atom/movable/the_target) + .=..() + var/obj/machinery/M = the_target + if(!istype(M)) + return FALSE + if(is_type_in_list(M, ignored_machine_types)) + return FALSE + if(!M.idle_power_usage && !M.active_power_usage && !(istype(M, /obj/machinery/power/apc) || istype(M, /obj/machinery/power/smes))) + return FALSE + if(locate(/mob/living/simple_mob/animal/solargrub_larva) in M) + return FALSE + if(M in ignored_targets) + return FALSE + return + +/datum/ai_holder/simple_mob/solargrub_larva/post_melee_attack(atom/A) + if(istype(A, /obj/machinery) && !istype(A, /obj/machinery/atmospherics/unary/vent_pump)) + if(ignored_targets.len > 3) + ignored_targets.Cut(1,1) + ignored_targets += A + /obj/machinery/abstract_grub_machine/New() ..() @@ -248,7 +284,7 @@ var/global/list/grub_machine_overlays = list() /obj/item/device/multitool/afterattack(obj/O, mob/user, proximity) if(proximity) if(istype(O, /obj/machinery)) - var/mob/living/simple_mob/solargrub_larva/grub = locate() in O + var/mob/living/simple_mob/animal/solargrub_larva/grub = locate() in O if(grub) grub.eject_from_machine(O) to_chat(user, "You disturb a grub nesting in \the [O]!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm index d09a4c7adf..a7ef2c1c8f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm @@ -1,5 +1,6 @@ /mob/living/simple_mob/vore + mob_class = MOB_CLASS_ANIMAL mob_bump_flag = 0 /mob/living/simple_mob/vore/aggressive - mob_bump_flag = 1 \ No newline at end of file + mob_bump_flag = HEAVY \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm index 5daea0571e..afe1d73135 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm @@ -1,3 +1,10 @@ +/datum/category_item/catalogue/fauna/wolf //TODO: VIRGO_LORE_WRITING_WIP + name = "Creature - Wolf" + desc = "Some sort of wolf, a descendent or otherwise of regular Earth canidae. They look almost exactly like their \ + Earth counterparts, except for the fact that their fur is a uniform grey. Some do show signs of unique coloration, and they \ + love to nip and bite at things, as well as sniffing around. They seem to mark their territory by way of scent-marking/urinating on things." + value = CATALOGUER_REWARD_MEDIUM + /mob/living/simple_mob/animal/wolf name = "grey wolf" desc = "My, what big jaws it has!" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm index 9520d9a3ac..b5a3bc2c60 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm @@ -121,7 +121,7 @@ health = 80 // Increase health to compensate maxHealth = 80 */ - +/* /mob/living/simple_mob/animal/space/mimic vore_active = 1 // NO VORE SPRITES @@ -130,7 +130,7 @@ // Overrides to non-vore version maxHealth = 60 health = 60 - +*/ /mob/living/simple_mob/animal/passive/cat vore_active = 1 // NO VORE SPRITES diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d13533a758..ec032d7b46 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -146,7 +146,7 @@ return M return 0 -/mob/proc/movement_delay() +/mob/proc/movement_delay(oldloc, direct) return 0 /mob/proc/Life() @@ -317,7 +317,7 @@ set src in usr if(usr != src) usr << "No." - var/msg = sanitize(input(usr,"Set the flavor text in your 'examine' verb. Can also be used for OOC notes about your character.","Flavor Text",html_decode(flavor_text)) as message|null, extra = 0) + var/msg = sanitize(input(usr,"Set the flavor text in your 'examine' verb.","Flavor Text",html_decode(flavor_text)) as message|null, extra = 0) //VOREStation Edit: separating out OOC notes if(msg != null) flavor_text = msg @@ -431,7 +431,7 @@ src << browse('html/changelog.html', "window=changes;size=675x650") if(prefs.lastchangelog != changelog_hash) prefs.lastchangelog = changelog_hash - prefs.save_preferences() + SScharacter_setup.queue_preferences_save(prefs) winset(src, "rpane.changelog", "background-color=none;font-style=;") /mob/verb/observe() @@ -730,7 +730,7 @@ listed_turf = null else if(statpanel("Turf")) - stat("\icon[listed_turf]", listed_turf.name) + stat(listed_turf) for(var/atom/A in listed_turf) if(!A.mouse_opacity) continue diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index b335faf1d3..7ad90dfc43 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -174,6 +174,12 @@ assailant.visible_message("[assailant] covers [affecting]'s eyes!") if(affecting.eye_blind < 3) affecting.Blind(3) + //TFF 12/8/19 VoreStation Addition Start + if(BP_HEAD) + if(force_down) + if(announce) + assailant.visible_message("[assailant] sits on [target]'s head!") + //VoreStation Addition End /obj/item/weapon/grab/attack_self() return s_click(hud) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 6c31f54f8e..a60586bbf2 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -147,6 +147,8 @@ proc/getsensorlevel(A) var/miss_chance = 10 if (zone in base_miss_chance) miss_chance = base_miss_chance[zone] + if (zone == "eyes" || zone == "mouth") + miss_chance = base_miss_chance["head"] miss_chance = max(miss_chance + miss_chance_mod, 0) if(prob(miss_chance)) if(prob(70)) @@ -158,7 +160,7 @@ proc/getsensorlevel(A) /proc/stars(n, pr) if (pr == null) pr = 25 - if (pr <= 0) + if (pr < 0) return null else if (pr >= 100) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index d06740bd8d..f1dbbfde71 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -84,7 +84,7 @@ /client/verb/drop_item() set hidden = 1 - if(!isrobot(mob) && mob.stat == CONSCIOUS && isturf(mob.loc)) + if(!isrobot(mob) && mob.stat == CONSCIOUS && (isturf(mob.loc) || isbelly(mob.loc))) // VOREStation Edit: dropping in bellies return mob.drop_item() return @@ -203,7 +203,7 @@ mob.move_delay += config.run_speed if("walk") mob.move_delay += config.walk_speed - mob.move_delay += mob.movement_delay() + mob.move_delay += mob.movement_delay(n, direct) if(istype(mob.buckled, /obj/vehicle) || istype(mob.buckled, /mob)) //VOREStation Edit: taur riding. I think. //manually set move_delay for vehicles so we don't inherit any mob movement penalties @@ -314,8 +314,10 @@ switch(mob.incorporeal_move) if(1) var/turf/T = get_step(mob, direct) + if(!T) + return if(mob.check_holy(T)) - mob << "You cannot get past holy grounds while you are in this plane of existence!" + to_chat(mob, "You cannot get past holy grounds while you are in this plane of existence!") return else mob.forceMove(get_step(mob, direct)) diff --git a/code/modules/mob/new_player/login_vr.dm b/code/modules/mob/new_player/login_vr.dm new file mode 100644 index 0000000000..5eeb269bc5 --- /dev/null +++ b/code/modules/mob/new_player/login_vr.dm @@ -0,0 +1,2 @@ +/obj/effect/lobby_image + name = "VORE Station" \ No newline at end of file diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 6d3b768645..a8c613c725 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -114,7 +114,7 @@ if(href_list["observe"]) - if(alert(src,"Are you sure you wish to observe? You will have to wait 5 minute before being able to respawn!","Player Setup","Yes","No") == "Yes") //Vorestation edit + if(alert(src,"Are you sure you wish to observe? You will have to wait 60 seconds before being able to respawn!","Player Setup","Yes","No") == "Yes") //Vorestation edit - Rykka corrected to 60 seconds to match current spawn time if(!client) return 1 //Make a new mannequin quickly, and allow the observer to take the appearance @@ -169,13 +169,15 @@ ViewManifest() if(href_list["SelectedJob"]) - + + /* Vorestation Removal Start //Prevents people rejoining as same character. for (var/mob/living/carbon/human/C in mob_list) var/char_name = client.prefs.real_name if(char_name == C.real_name) usr << "There is a character that already exists with the same name - [C.real_name], please join with a different one, or use Quit the Round with the previous character." //VOREStation Edit return + */ //Vorestation Removal End if(!config.enter_allowed) usr << "There is an administrative lock on entering the game!" @@ -309,7 +311,7 @@ var/savefile/F = get_server_news() if(F) client.prefs.lastnews = md5(F["body"]) - client.prefs.save_preferences() + SScharacter_setup.queue_preferences_save(client.prefs) var/dat = "
      " dat += "

      [F["title"]]

      " @@ -385,7 +387,7 @@ return // Equip our custom items only AFTER deploying to spawn points eh? - equip_custom_items(character) + //equip_custom_items(character) //VOREStation Removal //character.apply_traits() //VOREStation Removal diff --git a/code/modules/mob/new_player/new_player_vr.dm b/code/modules/mob/new_player/new_player_vr.dm index 95a610150e..b18e66fd83 100644 --- a/code/modules/mob/new_player/new_player_vr.dm +++ b/code/modules/mob/new_player/new_player_vr.dm @@ -1,6 +1,11 @@ /mob/new_player/proc/spawn_checks_vr() var/pass = TRUE + //No Flavor Text + if (config.require_flavor && client && client.prefs && client.prefs.flavor_texts && !client.prefs.flavor_texts["general"]) + to_chat(src,"Please set your general flavor text to give a basic description of your character. Set it using the 'Set Flavor text' button on the 'General' tab in character setup, and choosing 'General' category.") + pass = FALSE + //No OOC notes if (config.allow_Metadata && client && client.prefs && (isnull(client.prefs.metadata) || length(client.prefs.metadata) < 15)) to_chat(src,"Please set informative OOC notes related to ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup.") @@ -17,7 +22,7 @@ to_chat(src,"You have not set your scale yet. Do this on the VORE tab in character setup.") //Can they play? - if(!is_alien_whitelisted(src,all_species[client.prefs.species]) && (all_species[client.prefs.species].spawn_flags & SPECIES_WHITELIST_SELECTABLE)) + if(!is_alien_whitelisted(src,all_species[client.prefs.species]) && !check_rights(R_ADMIN, 0)) pass = FALSE to_chat(src,"You are not allowed to spawn in as this species.") diff --git a/code/modules/mob/new_player/preferences_setup_vr.dm b/code/modules/mob/new_player/preferences_setup_vr.dm index a690c27170..8b1760c65c 100644 --- a/code/modules/mob/new_player/preferences_setup_vr.dm +++ b/code/modules/mob/new_player/preferences_setup_vr.dm @@ -22,4 +22,8 @@ stamp.Scale(stamp.Width()*size_multiplier,stamp.Height()*size_multiplier) preview_icon.Blend(stamp, ICON_OVERLAY, 112-stamp.Width()/2, 5) - preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. \ No newline at end of file + preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. + +//TFF 5/8/19 - add randomised sensor setting for random button clicking +/datum/preferences/randomize_appearance_and_body_for(var/mob/living/carbon/human/H) + sensorpref = rand(1,5) \ No newline at end of file diff --git a/code/modules/mob/new_player/sprite_accessories_vr.dm b/code/modules/mob/new_player/sprite_accessories_vr.dm index 4ee7d5e01c..0ab4cc128b 100644 --- a/code/modules/mob/new_player/sprite_accessories_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_vr.dm @@ -8,7 +8,13 @@ //var/icon_add = 'icons/mob/human_face.dmi' //Already defined in sprite_accessories.dm line 49. var/color_blend_mode = ICON_MULTIPLY - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE) //This lets all races use the default hairstyles. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the default hairstyles. + + astolfo + name = "Astolfo" + icon = 'icons/mob/human_face_vr.dmi' + icon_add = 'icons/mob/human_face_vr_add.dmi' + icon_state = "hair_astolfo" awoohair name = "Shoulder-length Messy" @@ -81,14 +87,14 @@ name = "Bald" icon_state = "bald" gender = MALE - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE) //Lets all the races be bald if they want. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //Lets all the races be bald if they want. ponytail6_fixed //Eggnerd's done with waiting for upstream fixes lmao. name = "Ponytail 6 but fixed" icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "hair_ponytail6" - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA,SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE) + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA,SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) una_hood name = "Cobra Hood" @@ -471,13 +477,13 @@ /datum/sprite_accessory/facial_hair icon = 'icons/mob/human_face_or_vr.dmi' var/color_blend_mode = ICON_MULTIPLY - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE) //This lets all races use the facial hair styles. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the facial hair styles. shaved name = "Shaved" icon_state = "bald" gender = NEUTER - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE) //This needed to be manually defined, apparantly. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This needed to be manually defined, apparantly. vulp_none @@ -1058,4 +1064,36 @@ name = "Heterochromia" icon_state = "heterochromia" body_parts = list(BP_HEAD) - species_allowed = list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE) //This lets all races use the default hairstyles. + species_allowed = list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the default hairstyles. + + werewolf_nose + name = "Werewolf nose" + icon = 'icons/mob/species/werebeast/werebeast_markings.dmi' + icon_state = "werewolf_nose" + color_blend_mode = ICON_MULTIPLY + body_parts = list(BP_HEAD) + species_allowed = list(SPECIES_WEREBEAST) + + werewolf_face + name = "Werewolf face" + icon = 'icons/mob/species/werebeast/werebeast_markings.dmi' + icon_state = "werewolf" + color_blend_mode = ICON_MULTIPLY + body_parts = list(BP_HEAD) + species_allowed = list(SPECIES_WEREBEAST) + + werewolf_belly + name = "Werewolf belly" + icon = 'icons/mob/species/werebeast/werebeast_markings.dmi' + icon_state = "werewolf" + color_blend_mode = ICON_MULTIPLY + body_parts = list(BP_GROIN,BP_TORSO) + species_allowed = list(SPECIES_WEREBEAST) + + werewolf_socks + name = "Werewolf socks" + icon = 'icons/mob/species/werebeast/werebeast_markings.dmi' + icon_state = "werewolf" + color_blend_mode = ICON_MULTIPLY + body_parts = list(BP_L_FOOT,BP_R_FOOT,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_L_HAND,BP_R_HAND) + species_allowed = list(SPECIES_WEREBEAST) \ No newline at end of file diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 29263adeaf..947853055c 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -21,8 +21,12 @@ if(say_disabled) //This is here to try to identify lag problems usr << "Speech is currently admin-disabled." return - + + //VOREStation Edit Start + if(muffled) + return me_verb_subtle(message) message = sanitize_or_reflect(message,src) //VOREStation Edit - Reflect too-long messages (within reason) + //VOREStation Edit End set_typing_indicator(FALSE) if(use_me) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index c1d0f9de64..6f05b298e6 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -91,7 +91,7 @@ add_language(LANGUAGE_SCHECHI, 1) add_language(LANGUAGE_SIGN, 1) add_language(LANGUAGE_TERMINUS, 1) - add_language(LANGUAGE_ZADDAT = 0) + add_language(LANGUAGE_ZADDAT, 0) // Lorefolks say it may be so. if(O.client && O.client.prefs) @@ -182,7 +182,7 @@ var/datum/preferences/B = O.client.prefs for(var/language in B.alternate_languages) O.add_language(language) - O.resize(B.size_multiplier, animate = FALSE) //VOREStation Addition: add size prefs to borgs + O.resize(B.size_multiplier, animate = TRUE) //VOREStation Addition: add size prefs to borgs O.fuzzy = B.fuzzy //VOREStation Addition: add size prefs to borgs callHook("borgify", list(O)) diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm new file mode 100644 index 0000000000..28173c09d0 --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm @@ -0,0 +1,65 @@ +var/global/ntnrc_uid = 0 + +/datum/ntnet_conversation/ + var/id = null + var/title = "Untitled Conversation" + var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator, + var/list/messages = list() + var/list/clients = list() + var/password + +/datum/ntnet_conversation/New() + id = ntnrc_uid + ntnrc_uid++ + if(ntnet_global) + ntnet_global.chat_channels.Add(src) + ..() + +/datum/ntnet_conversation/proc/add_message(var/message, var/username) + message = "[stationtime2text()] [username]: [message]" + messages.Add(message) + trim_message_list() + +/datum/ntnet_conversation/proc/add_status_message(var/message) + messages.Add("[stationtime2text()] -!- [message]") + trim_message_list() + +/datum/ntnet_conversation/proc/trim_message_list() + if(messages.len <= 50) + return + messages.Cut(1, (messages.len-49)) + +/datum/ntnet_conversation/proc/add_client(var/datum/computer_file/program/chatclient/C) + if(!istype(C)) + return + clients.Add(C) + add_status_message("[C.username] has joined the channel.") + // No operator, so we assume the channel was empty. Assign this user as operator. + if(!operator) + changeop(C) + +/datum/ntnet_conversation/proc/remove_client(var/datum/computer_file/program/chatclient/C) + if(!istype(C) || !(C in clients)) + return + clients.Remove(C) + add_status_message("[C.username] has left the channel.") + + // Channel operator left, pick new operator + if(C == operator) + operator = null + if(clients.len) + var/datum/computer_file/program/chatclient/newop = pick(clients) + changeop(newop) + + +/datum/ntnet_conversation/proc/changeop(var/datum/computer_file/program/chatclient/newop) + if(istype(newop)) + operator = newop + add_status_message("Channel operator status transferred to [newop.username].") + +/datum/ntnet_conversation/proc/change_title(var/newtitle, var/datum/computer_file/program/chatclient/client) + if(operator != client) + return 0 // Not Authorised + + add_status_message("[client.username] has changed channel title from [title] to [newtitle]") + title = newtitle \ No newline at end of file diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm new file mode 100644 index 0000000000..be42cec23f --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNet.dm @@ -0,0 +1,184 @@ +var/global/datum/ntnet/ntnet_global = new() + + +// This is the NTNet datum. There can be only one NTNet datum in game at once. Modular computers read data from this. +/datum/ntnet/ + var/list/relays = list() + var/list/logs = list() + var/list/available_station_software = list() + var/list/available_antag_software = list() + var/list/available_news = list() + var/list/chat_channels = list() + var/list/fileservers = list() + var/list/email_accounts = list() // I guess we won't have more than 999 email accounts active at once in single round, so this will do until Servers are implemented someday. + var/list/banned_nids = list() + // Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly. + // High values make displaying logs much laggier. + var/setting_maxlogcount = 100 + + // These only affect wireless. LAN (consoles) are unaffected since it would be possible to create scenario where someone turns off NTNet, and is unable to turn it back on since it refuses connections + var/setting_softwaredownload = 1 + var/setting_peertopeer = 1 + var/setting_communication = 1 + var/setting_systemcontrol = 1 + var/setting_disabled = 0 // Setting to 1 will disable all wireless, independently on relays status. + + var/intrusion_detection_enabled = 1 // Whether the IDS warning system is enabled + var/intrusion_detection_alarm = 0 // Set when there is an IDS warning due to malicious (antag) software. + + +// If new NTNet datum is spawned, it replaces the old one. +/datum/ntnet/New() + if(ntnet_global && (ntnet_global != src)) + ntnet_global = src // There can be only one. + for(var/obj/machinery/ntnet_relay/R in machines) + relays.Add(R) + R.NTNet = src + build_software_lists() + build_news_list() + build_emails_list() + add_log("NTNet logging system activated.") + +/datum/ntnet/proc/add_log_with_ids_check(var/log_string, var/obj/item/weapon/computer_hardware/network_card/source = null) + if(intrusion_detection_enabled) + add_log(log_string, source) + +// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional. +/datum/ntnet/proc/add_log(var/log_string, var/obj/item/weapon/computer_hardware/network_card/source = null) + var/log_text = "[stationtime2text()] - " + if(source) + log_text += "[source.get_network_tag()] - " + else + log_text += "*SYSTEM* - " + log_text += log_string + logs.Add(log_text) + + if(logs.len > setting_maxlogcount) + // We have too many logs, remove the oldest entries until we get into the limit + for(var/L in logs) + if(logs.len > setting_maxlogcount) + logs.Remove(L) + else + break + +/datum/ntnet/proc/check_banned(var/NID) + if(!relays || !relays.len) + return FALSE + + for(var/obj/machinery/ntnet_relay/R in relays) + if(R.operable()) + return (NID in banned_nids) + + return FALSE + +// Checks whether NTNet operates. If parameter is passed checks whether specific function is enabled. +/datum/ntnet/proc/check_function(var/specific_action = 0) + if(!relays || !relays.len) // No relays found. NTNet is down + return 0 + + var/operating = 0 + + // Check all relays. If we have at least one working relay, network is up. + for(var/obj/machinery/ntnet_relay/R in relays) + if(R.operable()) + operating = 1 + break + + if(setting_disabled) + return 0 + + if(specific_action == NTNET_SOFTWAREDOWNLOAD) + return (operating && setting_softwaredownload) + if(specific_action == NTNET_PEERTOPEER) + return (operating && setting_peertopeer) + if(specific_action == NTNET_COMMUNICATION) + return (operating && setting_communication) + if(specific_action == NTNET_SYSTEMCONTROL) + return (operating && setting_systemcontrol) + return operating + +// Builds lists that contain downloadable software. +/datum/ntnet/proc/build_software_lists() + available_station_software = list() + available_antag_software = list() + for(var/F in typesof(/datum/computer_file/program)) + var/datum/computer_file/program/prog = new F + // Invalid type (shouldn't be possible but just in case), invalid filetype (not executable program) or invalid filename (unset program) + if(!prog || !istype(prog) || prog.filename == "UnknownProgram" || prog.filetype != "PRG") + continue + // Check whether the program should be available for station/antag download, if yes, add it to lists. + if(prog.available_on_ntnet) + available_station_software.Add(prog) + if(prog.available_on_syndinet) + available_antag_software.Add(prog) + +// Builds lists that contain downloadable software. +/datum/ntnet/proc/build_news_list() + available_news = list() + for(var/F in typesof(/datum/computer_file/data/news_article/)) + var/datum/computer_file/data/news_article/news = new F(1) + if(news.stored_data) + available_news.Add(news) + +// Generates service email list. Currently only used by broadcaster service +/datum/ntnet/proc/build_emails_list() + for(var/F in subtypesof(/datum/computer_file/data/email_account/service)) + new F() + +// Attempts to find a downloadable file according to filename var +/datum/ntnet/proc/find_ntnet_file_by_name(var/filename) + for(var/datum/computer_file/program/P in available_station_software) + if(filename == P.filename) + return P + for(var/datum/computer_file/program/P in available_antag_software) + if(filename == P.filename) + return P + +// Resets the IDS alarm +/datum/ntnet/proc/resetIDS() + intrusion_detection_alarm = 0 + +/datum/ntnet/proc/toggleIDS() + resetIDS() + intrusion_detection_enabled = !intrusion_detection_enabled + +// Removes all logs +/datum/ntnet/proc/purge_logs() + logs = list() + add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-") + +// Updates maximal amount of stored logs. Use this instead of setting the number, it performs required checks. +/datum/ntnet/proc/update_max_log_count(var/lognumber) + if(!lognumber) + return 0 + // Trim the value if necessary + lognumber = between(MIN_NTNET_LOGS, lognumber, MAX_NTNET_LOGS) + setting_maxlogcount = lognumber + add_log("Configuration Updated. Now keeping [setting_maxlogcount] logs in system memory.") + +/datum/ntnet/proc/toggle_function(var/function) + if(!function) + return + function = text2num(function) + switch(function) + if(NTNET_SOFTWAREDOWNLOAD) + setting_softwaredownload = !setting_softwaredownload + add_log("Configuration Updated. Wireless network firewall now [setting_softwaredownload ? "allows" : "disallows"] connection to software repositories.") + if(NTNET_PEERTOPEER) + setting_peertopeer = !setting_peertopeer + add_log("Configuration Updated. Wireless network firewall now [setting_peertopeer ? "allows" : "disallows"] peer to peer network traffic.") + if(NTNET_COMMUNICATION) + setting_communication = !setting_communication + add_log("Configuration Updated. Wireless network firewall now [setting_communication ? "allows" : "disallows"] instant messaging and similar communication services.") + if(NTNET_SYSTEMCONTROL) + setting_systemcontrol = !setting_systemcontrol + add_log("Configuration Updated. Wireless network firewall now [setting_systemcontrol ? "allows" : "disallows"] remote control of station's systems.") + +/datum/ntnet/proc/does_email_exist(var/login) + for(var/datum/computer_file/data/email_account/A in ntnet_global.email_accounts) + if(A.login == login) + return 1 + return 0 + + + diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm new file mode 100644 index 0000000000..7c76fdd2e3 --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -0,0 +1,136 @@ +// Relays don't handle any actual communication. Global NTNet datum does that, relays only tell the datum if it should or shouldn't work. +/obj/machinery/ntnet_relay + name = "NTNet Quantum Relay" + desc = "A very complex router and transmitter capable of connecting electronic devices together. Looks fragile." + use_power = 2 + active_power_usage = 20000 //20kW, apropriate for machine that keeps massive cross-Zlevel wireless network operational. + idle_power_usage = 100 + icon_state = "bus" + anchored = 1 + density = 1 + var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame. + var/enabled = 1 // Set to 0 if the relay was turned off + var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack + var/list/dos_sources = list() // Backwards reference for qdel() stuff + + // Denial of Service attack variables + var/dos_overload = 0 // Amount of DoS "packets" in this relay's buffer + var/dos_capacity = 500 // Amount of DoS "packets" in buffer required to crash the relay + var/dos_dissipate = 1 // Amount of DoS "packets" dissipated over time. + + +// TODO: Implement more logic here. For now it's only a placeholder. +/obj/machinery/ntnet_relay/operable() + if(!..(EMPED)) + return 0 + if(dos_failure) + return 0 + if(!enabled) + return 0 + return 1 + +/obj/machinery/ntnet_relay/update_icon() + if(operable()) + icon_state = "bus" + else + icon_state = "bus_off" + +/obj/machinery/ntnet_relay/process() + if(operable()) + use_power = 2 + else + use_power = 1 + + if(dos_overload) + dos_overload = max(0, dos_overload - dos_dissipate) + + // If DoS traffic exceeded capacity, crash. + if((dos_overload > dos_capacity) && !dos_failure) + dos_failure = 1 + update_icon() + ntnet_global.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") + // If the DoS buffer reaches 0 again, restart. + if((dos_overload == 0) && dos_failure) + dos_failure = 0 + update_icon() + ntnet_global.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") + ..() + +/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = list() + data["enabled"] = enabled + data["dos_capacity"] = dos_capacity + data["dos_overload"] = dos_overload + data["dos_crashed"] = dos_failure + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_relay.tmpl", "NTNet Quantum Relay", 500, 300, state = state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/ntnet_relay/attack_hand(var/mob/living/user) + ui_interact(user) + +/obj/machinery/ntnet_relay/Topic(href, href_list) + if(..()) + return 1 + if(href_list["restart"]) + dos_overload = 0 + dos_failure = 0 + update_icon() + ntnet_global.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") + return 1 + else if(href_list["toggle"]) + enabled = !enabled + ntnet_global.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].") + update_icon() + return 1 + else if(href_list["purge"]) + ntnet_global.banned_nids.Cut() + ntnet_global.add_log("Manual override: Network blacklist cleared.") + return 1 + +/obj/machinery/ntnet_relay/New() + uid = gl_uid + gl_uid++ + component_parts = list() + component_parts += new /obj/item/stack/cable_coil(src,15) + component_parts += new /obj/item/weapon/circuitboard/ntnet_relay(src) + + if(ntnet_global) + ntnet_global.relays.Add(src) + NTNet = ntnet_global + ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") + ..() + +/obj/machinery/ntnet_relay/Destroy() + if(ntnet_global) + ntnet_global.relays.Remove(src) + ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]") + NTNet = null + for(var/datum/computer_file/program/ntnet_dos/D in dos_sources) + D.target = null + D.error = "Connection to quantum relay severed" + ..() + +/obj/machinery/ntnet_relay/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.is_screwdriver()) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + panel_open = !panel_open + to_chat(user, "You [panel_open ? "open" : "close"] the maintenance hatch") + return + if(W.is_crowbar()) + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + to_chat(user, "You disassemble \the [src]!") + + for(var/atom/movable/A in component_parts) + A.forceMove(src.loc) + new /obj/structure/frame(src.loc) + qdel(src) + return + ..() \ No newline at end of file diff --git a/code/modules/modular_computers/NTNet/emails/email_account.dm b/code/modules/modular_computers/NTNet/emails/email_account.dm new file mode 100644 index 0000000000..b5f9388714 --- /dev/null +++ b/code/modules/modular_computers/NTNet/emails/email_account.dm @@ -0,0 +1,82 @@ +/datum/computer_file/data/email_account/ + var/list/inbox = list() + var/list/spam = list() + var/list/deleted = list() + + var/login = "" + var/password = "" + var/can_login = TRUE // Whether you can log in with this account. Set to false for system accounts + var/suspended = FALSE // Whether the account is banned by the SA. + +/datum/computer_file/data/email_account/calculate_size() + size = 1 + for(var/datum/computer_file/data/email_message/stored_message in all_emails()) + stored_message.calculate_size() + size += stored_message.size + +/datum/computer_file/data/email_account/New() + ntnet_global.email_accounts.Add(src) + ..() + +/datum/computer_file/data/email_account/Destroy() + ntnet_global.email_accounts.Remove(src) + . = ..() + +/datum/computer_file/data/email_account/proc/all_emails() + return (inbox | spam | deleted) + +/datum/computer_file/data/email_account/proc/send_mail(var/recipient_address, var/datum/computer_file/data/email_message/message, var/relayed = 0) + var/datum/computer_file/data/email_account/recipient + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(account.login == recipient_address) + recipient = account + break + + if(!istype(recipient)) + return 0 + + if(!recipient.receive_mail(message, relayed)) + return + + ntnet_global.add_log_with_ids_check("EMAIL LOG: [login] -> [recipient.login] title: [message.title].") + return 1 + +/datum/computer_file/data/email_account/proc/receive_mail(var/datum/computer_file/data/email_message/received_message, var/relayed) + received_message.set_timestamp() + if(!ntnet_global.intrusion_detection_enabled) + inbox.Add(received_message) + return 1 + // Spam filters may occassionally let something through, or mark something as spam that isn't spam. + if(received_message.spam) + if(prob(98)) + spam.Add(received_message) + else + inbox.Add(received_message) + else + if(prob(1)) + spam.Add(received_message) + else + inbox.Add(received_message) + return 1 + +// Address namespace (@internal-services.nt) for email addresses with special purpose only!. +/datum/computer_file/data/email_account/service/ + can_login = FALSE + +/datum/computer_file/data/email_account/service/broadcaster/ + login = "broadcast@internal-services.nt" + +/datum/computer_file/data/email_account/service/broadcaster/receive_mail(var/datum/computer_file/data/email_message/received_message, var/relayed) + if(!istype(received_message) || relayed) + return 0 + // Possibly exploitable for user spamming so keep admins informed. + if(!received_message.spam) + log_and_message_admins("Broadcast email address used by [usr]. Message title: [received_message.title].") + + spawn(0) + for(var/datum/computer_file/data/email_account/email_account in ntnet_global.email_accounts) + var/datum/computer_file/data/email_message/new_message = received_message.clone() + send_mail(email_account.login, new_message, 1) + sleep(2) + + return 1 \ No newline at end of file diff --git a/code/modules/modular_computers/NTNet/emails/email_message.dm b/code/modules/modular_computers/NTNet/emails/email_message.dm new file mode 100644 index 0000000000..d499195db2 --- /dev/null +++ b/code/modules/modular_computers/NTNet/emails/email_message.dm @@ -0,0 +1,32 @@ +// Currently not actually represented in file systems, though the support for it is in place already. +/datum/computer_file/data/email_message/ + stored_data = "" + var/title = "" + var/source = "" + var/spam = FALSE + var/timestamp = "" + var/datum/computer_file/attachment = null + +/datum/computer_file/data/email_message/clone() + var/datum/computer_file/data/email_message/temp = ..() + temp.title = title + temp.source = source + temp.spam = spam + temp.timestamp = timestamp + if(attachment) + temp.attachment = attachment.clone() + return temp + + +// Turns /email_message/ file into regular /data/ file. +/datum/computer_file/data/email_message/proc/export() + var/datum/computer_file/data/dat = new/datum/computer_file/data() + dat.stored_data = "Received from [source] at [timestamp]." + dat.stored_data += "\[b\][title]\[/b\]" + dat.stored_data += stored_data + dat.calculate_size() + return dat + +/datum/computer_file/data/email_message/proc/set_timestamp() + timestamp = stationtime2text() + diff --git a/code/modules/modular_computers/_description.dm b/code/modules/modular_computers/_description.dm new file mode 100644 index 0000000000..2335ce08e7 --- /dev/null +++ b/code/modules/modular_computers/_description.dm @@ -0,0 +1,121 @@ +/* +Program-based computers, designed to replace computer3 project and eventually most consoles on station + + +1. Basic information +Program based computers will allow you to do multiple things from single computer. Each computer will have programs, with more being downloadable from NTNet (stationwide network with wireless coverage) +if user has apropriate ID card access. It will be possible to hack the computer by using an emag on it - the emag will have to be completely new and will be consumed on use, but it will +lift ALL locks on ALL installed programs, and allow download of programs even if your ID doesn't have access to them. Computers will have hard drives that can store files. +Files can be programs (datum/computer_file/program/ subtype) or data files (datum/computer_file/data/ subtypes). Program for sending files will be available that will allow transfer via NTNet. +NTNet coverage will be limited to station's Z level, but better network card (=more expensive and higher power use) will allow usage everywhere. Hard drives will have limited capacity for files +which will be related to how good hard drive you buy when purchasing the laptop. For storing more files USB-style drives will be buildable with Protolathe in research. + +2. Available devices +CONSOLES +Consoles will come in various pre-fabricated loadouts, each loadout starting with certain set of programs (aka Engineering console, Medical console, etc.), of course, more software may be downloaded. +Consoles won't usually have integrated battery, but the possibility to install one will exist for critical applications. Consoles are considered hardwired into NTNet network which means they +will have working coverage on higher speed (Ethernet is faster than Wi-Fi) and won't require wireless coverage to exist. +LAPTOPS +Laptops are middle ground between actual portable devices and full consoles. They offer certain level of mobility, as they can be closed, moved somewhere else and then opened again. +Laptops will by default have internal battery to power them, and may be recharged with rechargers. However, laptops rely on wireless NTNet coverage. Laptop HDDs are also designed with power efficiency +in mind, which means they sacrifice some storage space for higher battery life. Laptops may be dispensed from computer vendor machine, and may be customised before vending. For people which don't +want to rely on internal battery, tesla link exists that connects to APC, if one exists. +TABLETS +Tablets are smallest available devices, designed with full mobility in mind. Tablets have only weak CPU which means the software they can run is somewhat limited. They are also designed with high +battery life in mind, which means the hardware focuses on power efficiency rather than high performance. This is most visible with hard drives which have quite small storage capacity. +Tablets can't be equipped with tesla link, which means they have to be recharged manually. + + +3. Computer Hardware +Computers will come with basic hardware installed, with upgrades being selectable when purchasing the device. +Hard Drive: Stores data, mandatory for the computer to work +Network Card: Connects to NTNet +Battery: Internal power source that ensures the computer operates when not connected to APC. +Extras (those won't be installed by default, but can be bought) +ID Card Slot: Required for HoP-style programs to work. Access for security record-style programs is read from ID of user [RFID?] without requiring this +APC Tesla Relay: Wirelessly powers the device from APC. Consoles have it by default. Laptops can buy it. +Disk Drive: Allows usage of portable data disks. +Nano Printer: Allows the computer to scan paper contents and save them to file, as well as recycle papers and print stuff on it. + +4. NTNet +NTNet is stationwide network that allows users to download programs needed for their work. It will be possible to send any files to other active computers using relevant program (NTN Transfer). +NTNet is under jurisdiction of both Engineering and Research. Engineering is responsible for any repairs if necessary and research is responsible for monitoring. It is similar to PDA messaging. +Operation requires functional "NTNet Relay" which is by default placed on tcommsat. If the relay is damaged NTNet will be offline until it is replaced. Multiple relays bring extra redundancy, +if one is destroyed the second will take over. If all relays are gone it stops working, simple as that. NTNet may be altered via administration console available to Research Director. It is +possible to enable/disable Software Downloading, P2P file transfers and Communication (IC version of IRC, PDA messages for more than two people) + +5. Software +Software would almost exclusively use NanoUI modules. Few exceptions are text editor (uses similar screen as TCS IDE used for editing and classic HTML for previewing as Nano looks differently) +and similar programs which for some reason require HTML UI. Most software will be highly dependent on NTNet to work as laptops are not physically connected to the station's network. +What i plan to add: + +Note: XXXXDB programs will use ingame_manuals to display basic help for players, similar to how books, etc. do + +Basic - Software in this bundle is automagically preinstalled in every new computer + NTN Transfer - Allows P2P transfer of files to other computers that run this. + Configurator - Allows configuration of computer's hardware, basically status screen. + File Browser - Allows you to browse all files stored on the computer. Allows renaming/deleting of files. + TXT Editor - Allows you editing data files in text editor mode. + NanoPrint - Allows you to operate NanoPrinter hardware to print text files. + NTNRC Chat - NTNet Relay Chat client. Allows PDA-messaging style messaging for more than two users. Person which created the conversation is Host and has administrative privilegies (kicking, etc.) + NTNet News - Allows reading news from newscaster network. + +Engineering - Requires "Engineering" access on ID card (ie. CE, Atmostech, Engineer) + Alarm Monitor - Allows monitoring alarms, same as the stationbound one. + Power Monitor - Power monitoring computer, connects to sensors in same way as regular one does. + Atmospheric Control - Allows access to the Atmospherics Monitor Console that operates air alarms. Requires extra access: "Atmospherics" + RCON Remote Control Console - Allows access to the RCON Remote Control Console. Requires extra access: "Power Equipment" + EngiDB - Allows accessing NTNet information repository for information about engineering-related things. + +Medical - Requires "Medbay" access on ID card (ie. CMO, Doctor,..) + Medical Records Uplink - Allows editing/reading of medical records. Printing requires NanoPrinter hardware. + MediDB - Allows accessing NTNet information repository for information about medical procedures + ChemDB - Requires extra access: "Chemistry" - Downloads basic information about recipes from NTNet + +Research - Requires "Research and Development" access on ID card (ie. RD, Roboticist, etc.) + Research Server Monitor - Allows monitoring of research levels on RnD servers. (read only) + Robotics Monitor Console - Allows monitoring of robots and exosuits. Lockdown/Self-Destruct options are unavailable [balance reasons for malf/traitor AIs]. Requires extra access: "Robotics" + NTNRC Administration Console - Allows administrative access to NTNRC. This includes bypassing any channel passwords and enabling "invisible" mode for spying on conversations. Requires extra access: "Research Director" + NTNet Administration Console - Allows remote configuration of NTNet Relay - CAUTION: If NTNet is turned off it won't be possible to turn it on again from the computer, as operation requires NTNet to work! Requires extra access: "Research Director" + NTNet Monitor - Allows monitoring of NTNet and it's various components, including simplified network logs and system status. + +Security - Requires "Security" access on ID card (ie. HOS, Security officer, Detective) + Security Records Uplink - Allows editing/reading of security records. Printing requires Nanoprinter hardware. + LawDB - Allows accessing NTNet information repository for security information (corporate regulations) + Camera Uplink - Allows viewing cameras around the station. + +Command - Requires "Bridge" access on ID card (all heads) + Alertcon Access - Allows changing of alert levels. Red requires activation from two computers with two IDs similar to how those wall mounted devices do. + Employment Records Access - Allows reading of employment records. Printing requires NanoPrinter hardware. + Communication Console - Allows sending emergency messages to Central. + Emergency Shuttle Control Console - Allows calling/recalling the emergency shuttle. + Shuttle Control Console - Allows control of various shuttles around the station (mining, research, engineering) + +*REDACTED* - Can be downloaded from SyndiCorp servers, only via emagged devices. These files are very large and limited to laptops/consoles only. + SYSCRACK - Allows cracking of secure network terminals, such as, NTNet administration. The sysadmin will probably notice this. + SYSOVERRIDE - Allows hacking into any device connected to NTNet. User will notice this and may stop the hack by disconnecting from NTNet first. After hacking various options exist, such as stealing/deleting files. + SYSKILL - Tricks NTNet to force-disconnect a device. The sysadmin will probably notice this. + SYSDOS - Launches a Denial of Service attack on NTNet relay. Can DoS only one relay at once. Requires NTNet connection. After some time the relay crashes until attack stops. The sysadmin will probably notice this. + AIHACK - Hacks an AI, allowing you to upload/remove/modify a law even without relevant circuit board. The AI is alerted once the hack starts, and it takes a while for it to complete. Does not work on AIs with zeroth law. + COREPURGE - Deletes all files on the hard drive, including the undeletable ones. Something like software self-destruct for computer. + +6. Security +Laptops will be password-lockable. If password is set a MD5 hash of it is stored and password is required every time you turn on the laptop. +Passwords may be decrypted by using special Decrypter (protolathable, RDs office starts with one) device that will slowly decrypt the password. +Decryption time would be length_of_password * 30 seconds, with maximum being 9 minutes (due to battery life limitations, which is 10+ min). +If decrypted the password is cleared, so you can keep using your favorite password without people ever actually revealing it (for meta prevention reasons mostly). +Emagged laptops will have option to enable "Safe Encryption". If safely encrypted laptop is decrypted it loses it's emag status and 50% of files is deleted (randomly selected). + +7. System Administrator +System Administrator will be new job under Research. It's main specifics will be maintaining of computer systems on station, espicially from software side. +From IC perspective they'd probably know how to build a console or something given they work with computers, but they are mostly programmers/network experts. +They will have office in research, which will probably replace (and contain) the server room and part of the toxins storage which is currently oversized. +They will have access to DOWNLOAD (not run) all programs that exist on NTNet. They'll have fairly good amount of available programs, most of them being +administrative consoles and other very useful things. They'll also be able to monitor NTNet. There will probably be one or two job slots. + +8. IDS +With addition of various antag programs, IDS(Intrusion Detection System) will be added to NTNet. This system can be turned on/off via administration console. +If enabled, this system automatically detects any abnormality and triggers a warning that's visible on the NTNet status screen, as well as generating a security log. +IDS can be disabled by simple on/off switch in the configuration. + +*/ \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm new file mode 100644 index 0000000000..30d9cf6039 --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -0,0 +1,269 @@ +/obj/item/modular_computer/process() + if(!enabled) // The computer is turned off + last_power_usage = 0 + return 0 + + if(damage > broken_damage) + shutdown_computer() + return 0 + + if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) // Active program requires NTNet to run but we've just lost connection. Crash. + active_program.event_networkfailure(0) + + for(var/datum/computer_file/program/P in idle_threads) + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) + P.event_networkfailure(1) + + if(active_program) + if(active_program.program_state != PROGRAM_STATE_KILLED) + active_program.ntnet_status = get_ntnet_status() + active_program.computer_emagged = computer_emagged + active_program.process_tick() + else + active_program = null + + for(var/datum/computer_file/program/P in idle_threads) + if(P.program_state != PROGRAM_STATE_KILLED) + P.ntnet_status = get_ntnet_status() + P.computer_emagged = computer_emagged + P.process_tick() + else + idle_threads.Remove(P) + + handle_power() // Handles all computer power interaction + check_update_ui_need() + +// Used to perform preset-specific hardware changes. +/obj/item/modular_computer/proc/install_default_hardware() + return 1 + +// Used to install preset-specific programs +/obj/item/modular_computer/proc/install_default_programs() + return 1 + +/obj/item/modular_computer/New() + START_PROCESSING(SSobj, src) + install_default_hardware() + if(hard_drive) + install_default_programs() + update_icon() + update_verbs() + ..() + +/obj/item/modular_computer/Destroy() + kill_program(1) + STOP_PROCESSING(SSobj, src) + for(var/obj/item/weapon/computer_hardware/CH in src.get_all_components()) + uninstall_component(null, CH) + qdel(CH) + return ..() + +/obj/item/modular_computer/emag_act(var/remaining_charges, var/mob/user) + if(computer_emagged) + to_chat(user, "\The [src] was already emagged.") + return //NO_EMAG_ACT + else + computer_emagged = 1 + to_chat(user, "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message.") + return 1 + +/obj/item/modular_computer/update_icon() + icon_state = icon_state_unpowered + + overlays.Cut() + if(bsod) + overlays.Add("bsod") + return + if(!enabled) + if(icon_state_screensaver) + overlays.Add(icon_state_screensaver) + set_light(0) + return + set_light(light_strength) + if(active_program) + overlays.Add(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu) + if(active_program.program_key_state) + overlays.Add(active_program.program_key_state) + else + overlays.Add(icon_state_menu) + +/obj/item/modular_computer/proc/turn_on(var/mob/user) + if(bsod) + return + if(tesla_link) + tesla_link.enabled = 1 + var/issynth = issilicon(user) // Robots and AIs get different activation messages. + if(damage > broken_damage) + if(issynth) + to_chat(user, "You send an activation signal to \the [src], but it responds with an error code. It must be damaged.") + else + to_chat(user, "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.") + return + if(processor_unit && (apc_power(0) || battery_power(0))) // Battery-run and charged or non-battery but powered by APC. + if(issynth) + to_chat(user, "You send an activation signal to \the [src], turning it on") + else + to_chat(user, "You press the power button and start up \the [src]") + enable_computer(user) + + else // Unpowered + if(issynth) + to_chat(user, "You send an activation signal to \the [src] but it does not respond") + else + to_chat(user, "You press the power button but \the [src] does not respond") + +// Relays kill program request to currently active program. Use this to quit current program. +/obj/item/modular_computer/proc/kill_program(var/forced = 0) + if(active_program) + active_program.kill_program(forced) + active_program = null + var/mob/user = usr + if(user && istype(user)) + ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + update_icon() + +// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) +/obj/item/modular_computer/proc/get_ntnet_status(var/specific_action = 0) + if(network_card) + return network_card.get_signal(specific_action) + else + return 0 + +/obj/item/modular_computer/proc/add_log(var/text) + if(!get_ntnet_status()) + return 0 + return ntnet_global.add_log(text, network_card) + +/obj/item/modular_computer/proc/shutdown_computer(var/loud = 1) + kill_program(1) + for(var/datum/computer_file/program/P in idle_threads) + P.kill_program(1) + idle_threads.Remove(P) + if(loud) + visible_message("\The [src] shuts down.") + enabled = 0 + update_icon() + +/obj/item/modular_computer/proc/enable_computer(var/mob/user = null) + enabled = 1 + update_icon() + + // Autorun feature + var/datum/computer_file/data/autorun = hard_drive ? hard_drive.find_file_by_name("autorun") : null + if(istype(autorun)) + run_program(autorun.stored_data) + + if(user) + ui_interact(user) + +/obj/item/modular_computer/proc/minimize_program(mob/user) + if(!active_program || !processor_unit) + return + + idle_threads.Add(active_program) + active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs + SSnanoui.close_uis(active_program.NM ? active_program.NM : active_program) + active_program = null + update_icon() + if(istype(user)) + ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + + +/obj/item/modular_computer/proc/run_program(prog) + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) + + if(!P || !istype(P)) // Program not found or it's not executable program. + to_chat(user, "\The [src]'s screen shows \"I/O ERROR - Unable to run [prog]\" warning.") + return + + P.computer = src + + if(!P.is_supported_by_hardware(hardware_flag, 1, user)) + return + if(P in idle_threads) + P.program_state = PROGRAM_STATE_ACTIVE + active_program = P + idle_threads.Remove(P) + update_icon() + return + + if(idle_threads.len >= processor_unit.max_idle_programs+1) + to_chat(user, "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error") + return + + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. + to_chat(user, "\The [src]'s screen shows \"NETWORK ERROR - Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.") + return + + if(active_program) + minimize_program(user) + + if(P.run_program(user)) + active_program = P + update_icon() + return 1 + +/obj/item/modular_computer/proc/update_uis() + if(active_program) //Should we update program ui or computer ui? + SSnanoui.update_uis(active_program) + if(active_program.NM) + SSnanoui.update_uis(active_program.NM) + else + SSnanoui.update_uis(src) + +/obj/item/modular_computer/proc/check_update_ui_need() + var/ui_update_needed = 0 + if(battery_module) + var/batery_percent = battery_module.battery.percent() + if(last_battery_percent != batery_percent) //Let's update UI on percent change + ui_update_needed = 1 + last_battery_percent = batery_percent + + if(stationtime2text() != last_world_time) + last_world_time = stationtime2text() + ui_update_needed = 1 + + if(idle_threads.len) + var/list/current_header_icons = list() + for(var/datum/computer_file/program/P in idle_threads) + if(!P.ui_header) + continue + current_header_icons[P.type] = P.ui_header + if(!last_header_icons) + last_header_icons = current_header_icons + + else if(!listequal(last_header_icons, current_header_icons)) + last_header_icons = current_header_icons + ui_update_needed = 1 + else + for(var/x in last_header_icons|current_header_icons) + if(last_header_icons[x]!=current_header_icons[x]) + last_header_icons = current_header_icons + ui_update_needed = 1 + break + + if(ui_update_needed) + update_uis() + +// Used by camera monitor program +/obj/item/modular_computer/check_eye(var/mob/user) + if(active_program) + return active_program.check_eye(user) + else + return ..() + +/obj/item/modular_computer/proc/set_autorun(program) + if(!hard_drive) + return + var/datum/computer_file/data/autorun = hard_drive.find_file_by_name("autorun") + if(!istype(autorun)) + autorun = new/datum/computer_file/data() + autorun.filename = "autorun" + hard_drive.store_file(autorun) + if(autorun.stored_data == program) + autorun.stored_data = null + else + autorun.stored_data = program \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/damage.dm b/code/modules/modular_computers/computers/modular_computer/damage.dm new file mode 100644 index 0000000000..9084bad735 --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/damage.dm @@ -0,0 +1,55 @@ +/obj/item/modular_computer/examine(var/mob/user) + . = ..() + if(damage > broken_damage) + to_chat(user, "It is heavily damaged!") + else if(damage) + to_chat(user, "It is damaged.") + +/obj/item/modular_computer/proc/break_apart() + visible_message("\The [src] breaks apart!") + var/turf/newloc = get_turf(src) + new /obj/item/stack/material/steel(newloc, round(steel_sheet_cost/2)) + for(var/obj/item/weapon/computer_hardware/H in get_all_components()) + uninstall_component(null, H) + H.forceMove(newloc) + if(prob(25)) + H.take_damage(rand(10,30)) + qdel() + +/obj/item/modular_computer/take_damage(var/amount, var/component_probability, var/damage_casing = 1, var/randomize = 1) + if(randomize) + // 75%-125%, rand() works with integers, apparently. + amount *= (rand(75, 125) / 100.0) + amount = round(amount) + if(damage_casing) + damage += amount + damage = between(0, damage, max_damage) + + if(component_probability) + for(var/obj/item/weapon/computer_hardware/H in get_all_components()) + if(prob(component_probability)) + H.take_damage(round(amount / 2)) + + if(damage >= max_damage) + break_apart() + +// Stronger explosions cause serious damage to internal components +// Minor explosions are mostly mitigitated by casing. +/obj/item/modular_computer/ex_act(var/severity) + take_damage(rand(100,200) / severity, 30 / severity) + +// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components +/obj/item/modular_computer/emp_act(var/severity) + take_damage(rand(100,200) / severity, 50 / severity, 0) + +// "Stun" weapons can cause minor damage to components (short-circuits?) +// "Burn" damage is equally strong against internal components and exterior casing +// "Brute" damage mostly damages the casing. +/obj/item/modular_computer/bullet_act(var/obj/item/projectile/Proj) + switch(Proj.damage_type) + if(BRUTE) + take_damage(Proj.damage, Proj.damage / 2) + if(HALLOSS) + take_damage(Proj.damage, Proj.damage / 3, 0) + if(BURN) + take_damage(Proj.damage, Proj.damage / 1.5) \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/hardware.dm b/code/modules/modular_computers/computers/modular_computer/hardware.dm new file mode 100644 index 0000000000..38545e8176 --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/hardware.dm @@ -0,0 +1,139 @@ +// Attempts to install the hardware into apropriate slot. +/obj/item/modular_computer/proc/try_install_component(var/mob/living/user, var/obj/item/weapon/computer_hardware/H, var/found = 0) + // "USB" flash drive. + if(istype(H, /obj/item/weapon/computer_hardware/hard_drive/portable)) + if(portable_drive) + to_chat(user, "This computer's portable drive slot is already occupied by \the [portable_drive].") + return + found = 1 + portable_drive = H + else if(istype(H, /obj/item/weapon/computer_hardware/hard_drive)) + if(hard_drive) + to_chat(user, "This computer's hard drive slot is already occupied by \the [hard_drive].") + return + found = 1 + hard_drive = H + else if(istype(H, /obj/item/weapon/computer_hardware/network_card)) + if(network_card) + to_chat(user, "This computer's network card slot is already occupied by \the [network_card].") + return + found = 1 + network_card = H + else if(istype(H, /obj/item/weapon/computer_hardware/nano_printer)) + if(nano_printer) + to_chat(user, "This computer's nano printer slot is already occupied by \the [nano_printer].") + return + found = 1 + nano_printer = H + else if(istype(H, /obj/item/weapon/computer_hardware/card_slot)) + if(card_slot) + to_chat(user, "This computer's card slot is already occupied by \the [card_slot].") + return + found = 1 + card_slot = H + else if(istype(H, /obj/item/weapon/computer_hardware/battery_module)) + if(battery_module) + to_chat(user, "This computer's battery slot is already occupied by \the [battery_module].") + return + found = 1 + battery_module = H + else if(istype(H, /obj/item/weapon/computer_hardware/processor_unit)) + if(processor_unit) + to_chat(user, "This computer's processor slot is already occupied by \the [processor_unit].") + return + found = 1 + processor_unit = H + else if(istype(H, /obj/item/weapon/computer_hardware/tesla_link)) + if(tesla_link) + to_chat(user, "This computer's tesla link slot is already occupied by \the [tesla_link].") + return + found = 1 + tesla_link = H + if(found) + to_chat(user, "You install \the [H] into \the [src]") + H.holder2 = src + user.drop_from_inventory(H) + H.forceMove(src) + update_verbs() + +// Uninstalls component. Found and Critical vars may be passed by parent types, if they have additional hardware. +/obj/item/modular_computer/proc/uninstall_component(var/mob/living/user, var/obj/item/weapon/computer_hardware/H, var/found = 0, var/critical = 0) + if(portable_drive == H) + portable_drive = null + found = 1 + if(hard_drive == H) + hard_drive = null + found = 1 + critical = 1 + if(network_card == H) + network_card = null + found = 1 + if(nano_printer == H) + nano_printer = null + found = 1 + if(card_slot == H) + card_slot = null + found = 1 + if(battery_module == H) + battery_module = null + found = 1 + if(processor_unit == H) + processor_unit = null + found = 1 + critical = 1 + if(tesla_link == H) + tesla_link = null + found = 1 + if(found) + if(user) + to_chat(user, "You remove \the [H] from \the [src].") + H.forceMove(get_turf(src)) + H.holder2 = null + update_verbs() + if(critical && enabled) + if(user) + to_chat(user, "\The [src]'s screen freezes for few seconds and then displays an \"HARDWARE ERROR: Critical component disconnected. Please verify component connection and reboot the device. If the problem persists contact technical support for assistance.\" warning.") + shutdown_computer() + update_icon() + + +// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null +/obj/item/modular_computer/proc/find_hardware_by_name(var/name) + if(portable_drive && (portable_drive.name == name)) + return portable_drive + if(hard_drive && (hard_drive.name == name)) + return hard_drive + if(network_card && (network_card.name == name)) + return network_card + if(nano_printer && (nano_printer.name == name)) + return nano_printer + if(card_slot && (card_slot.name == name)) + return card_slot + if(battery_module && (battery_module.name == name)) + return battery_module + if(processor_unit && (processor_unit.name == name)) + return processor_unit + if(tesla_link && (tesla_link.name == name)) + return tesla_link + return null + +// Returns list of all components +/obj/item/modular_computer/proc/get_all_components() + var/list/all_components = list() + if(hard_drive) + all_components.Add(hard_drive) + if(network_card) + all_components.Add(network_card) + if(portable_drive) + all_components.Add(portable_drive) + if(nano_printer) + all_components.Add(nano_printer) + if(card_slot) + all_components.Add(card_slot) + if(battery_module) + all_components.Add(battery_module) + if(processor_unit) + all_components.Add(processor_unit) + if(tesla_link) + all_components.Add(tesla_link) + return all_components \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/interaction.dm b/code/modules/modular_computers/computers/modular_computer/interaction.dm new file mode 100644 index 0000000000..bf70bb7d28 --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/interaction.dm @@ -0,0 +1,200 @@ +/obj/item/modular_computer/proc/update_verbs() + verbs.Cut() + if(portable_drive) + verbs |= /obj/item/modular_computer/verb/eject_usb + if(card_slot) + verbs |= /obj/item/modular_computer/verb/eject_id + verbs |= /obj/item/modular_computer/verb/emergency_shutdown + +// Forcibly shut down the device. To be used when something bugs out and the UI is nonfunctional. +/obj/item/modular_computer/verb/emergency_shutdown() + set name = "Forced Shutdown" + set category = "Object" + set src in view(1) + + if(usr.incapacitated() || !istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(!Adjacent(usr)) + to_chat(usr, "You can't reach it.") + return + + if(enabled) + bsod = 1 + update_icon() + shutdown_computer() + to_chat(usr, "You press a hard-reset button on \the [src]. It displays a brief debug screen before shutting down.") + spawn(2 SECONDS) + bsod = 0 + update_icon() + + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/modular_computer/verb/eject_id() + set name = "Eject ID" + set category = "Object" + set src in view(1) + + if(usr.incapacitated() || !istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(!Adjacent(usr)) + to_chat(usr, "You can't reach it.") + return + + proc_eject_id(usr) + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/modular_computer/verb/eject_usb() + set name = "Eject Portable Storage" + set category = "Object" + set src in view(1) + + if(usr.incapacitated() || !istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(!Adjacent(usr)) + to_chat(usr, "You can't reach it.") + return + + proc_eject_usb(usr) + +/obj/item/modular_computer/proc/proc_eject_id(mob/user) + if(!user) + user = usr + + if(!card_slot) + to_chat(user, "\The [src] does not have an ID card slot") + return + + if(!card_slot.stored_card) + to_chat(user, "There is no card in \the [src]") + return + + if(active_program) + active_program.event_idremoved(0) + + for(var/datum/computer_file/program/P in idle_threads) + P.event_idremoved(1) + + card_slot.stored_card.forceMove(get_turf(src)) + card_slot.stored_card = null + update_uis() + to_chat(user, "You remove the card from \the [src]") + + +/obj/item/modular_computer/proc/proc_eject_usb(mob/user) + if(!user) + user = usr + + if(!portable_drive) + to_chat(user, "There is no portable device connected to \the [src].") + return + + uninstall_component(user, portable_drive) + update_uis() + +/obj/item/modular_computer/attack_ghost(var/mob/observer/ghost/user) + if(enabled) + ui_interact(user) + else if(check_rights(R_ADMIN, 0, user)) + var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") + if(response == "Yes") + turn_on(user) + +/obj/item/modular_computer/attack_ai(var/mob/user) + return attack_self(user) + +/obj/item/modular_computer/attack_hand(var/mob/user) + if(anchored) + return attack_self(user) + return ..() + +// On-click handling. Turns on the computer if it's off and opens the GUI. +/obj/item/modular_computer/attack_self(var/mob/user) + if(enabled && screen_on) + ui_interact(user) + else if(!enabled && screen_on) + turn_on(user) + +/obj/item/modular_computer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/weapon/card/id)) // ID Card, try to insert it. + var/obj/item/weapon/card/id/I = W + if(!card_slot) + to_chat(user, "You try to insert \the [I] into \the [src], but it does not have an ID card slot installed.") + return + + if(card_slot.stored_card) + to_chat(user, "You try to insert \the [I] into \the [src], but it's ID card slot is occupied.") + return + user.drop_from_inventory(I) + card_slot.stored_card = I + I.forceMove(src) + update_uis() + to_chat(user, "You insert \the [I] into \the [src].") + return + if(istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/weapon/paper_bundle)) + if(!nano_printer) + return + nano_printer.attackby(W, user) + if(istype(W, /obj/item/weapon/computer_hardware)) + var/obj/item/weapon/computer_hardware/C = W + if(C.hardware_size <= max_hardware_size) + try_install_component(user, C) + else + to_chat(user, "This component is too large for \the [src].") + if(W.is_wrench()) + var/list/components = get_all_components() + if(components.len) + to_chat(user, "Remove all components from \the [src] before disassembling it.") + return + new /obj/item/stack/material/steel( get_turf(src.loc), steel_sheet_cost ) + src.visible_message("\The [src] has been disassembled by [user].") + qdel(src) + return + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(!WT.isOn()) + to_chat(user, "\The [W] is off.") + return + + if(!damage) + to_chat(user, "\The [src] does not require repairs.") + return + + to_chat(user, "You begin repairing damage to \the [src]...") + if(WT.remove_fuel(round(damage/75)) && do_after(usr, damage/10)) + damage = 0 + to_chat(user, "You repair \the [src].") + return + + if(W.is_screwdriver()) + var/list/all_components = get_all_components() + if(!all_components.len) + to_chat(user, "This device doesn't have any components installed.") + return + var/list/component_names = list() + for(var/obj/item/weapon/computer_hardware/H in all_components) + component_names.Add(H.name) + + var/choice = input(usr, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names + + if(!choice) + return + + if(!Adjacent(usr)) + return + + var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(choice) + + if(!H) + return + + uninstall_component(user, H) + + return + + ..() \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/power.dm b/code/modules/modular_computers/computers/modular_computer/power.dm new file mode 100644 index 0000000000..48871619fa --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/power.dm @@ -0,0 +1,51 @@ +/obj/item/modular_computer/proc/power_failure(var/malfunction = 0) + if(enabled) // Shut down the computer + visible_message("\The [src]'s screen flickers briefly and then goes dark.") + if(active_program) + active_program.event_powerfailure(0) + for(var/datum/computer_file/program/PRG in idle_threads) + PRG.event_powerfailure(1) + shutdown_computer(0) + +// Tries to use power from battery. Passing 0 as parameter results in this proc returning whether battery is functional or not. +/obj/item/modular_computer/proc/battery_power(var/power_usage = 0) + apc_powered = FALSE + if(!battery_module || !battery_module.check_functionality() || battery_module.battery.charge <= 0) + return FALSE + if(battery_module.battery.use(power_usage * CELLRATE) || ((power_usage == 0) && battery_module.battery.charge)) + return TRUE + return FALSE + +// Tries to use power from APC, if present. +/obj/item/modular_computer/proc/apc_power(var/power_usage = 0) + apc_powered = TRUE + // Tesla link was originally limited to machinery only, but this probably works too, and the benefit of being able to power all devices from an APC outweights + // the possible minor performance loss. + if(!tesla_link || !tesla_link.check_functionality()) + return FALSE + var/area/A = get_area(src) + if(!istype(A) || !A.powered(EQUIP)) + return FALSE + + // At this point, we know that APC can power us for this tick. Check if we also need to charge our battery, and then actually use the power. + if(battery_module && (battery_module.battery.charge < battery_module.battery.maxcharge) && (power_usage > 0)) + power_usage += tesla_link.passive_charging_rate + battery_module.battery.give(tesla_link.passive_charging_rate * CELLRATE) + + A.use_power(power_usage, EQUIP) + return TRUE + +// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged +/obj/item/modular_computer/proc/handle_power() + var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage + for(var/obj/item/weapon/computer_hardware/H in get_all_components()) + if(H.enabled) + power_usage += H.power_usage + last_power_usage = power_usage + + // First tries to charge from an APC, if APC is unavailable switches to battery power. If neither works the computer fails. + if(apc_power(power_usage)) + return + if(battery_power(power_usage)) + return + power_failure() \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/ui.dm b/code/modules/modular_computers/computers/modular_computer/ui.dm new file mode 100644 index 0000000000..81081c0cb1 --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/ui.dm @@ -0,0 +1,155 @@ +// Operates NanoUI +/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!screen_on || !enabled) + if(ui) + ui.close() + return 0 + if(!apc_power(0) && !battery_power(0)) + if(ui) + ui.close() + return 0 + + // If we have an active program switch to it now. + if(active_program) + if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now. + ui.close() + active_program.ui_interact(user) + return + + // We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it. + // This screen simply lists available programs and user may select them. + if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len) + visible_message("\The [src] beeps three times, it's screen displaying \"DISK ERROR\" warning.") + return // No HDD, No HDD files list or no stored files. Something is very broken. + + var/datum/computer_file/data/autorun = hard_drive.find_file_by_name("autorun") + + var/list/data = get_header_data() + + var/list/programs = list() + for(var/datum/computer_file/program/P in hard_drive.stored_files) + var/list/program = list() + program["name"] = P.filename + program["desc"] = P.filedesc + program["icon"] = P.program_menu_icon + program["autorun"] = (istype(autorun) && (autorun.stored_data == P.filename)) ? 1 : 0 + if(P in idle_threads) + program["running"] = 1 + programs.Add(list(program)) + + data["programs"] = programs + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "laptop_mainscreen.tmpl", "NTOS Main Menu", 400, 500) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// Handles user's GUI input +/obj/item/modular_computer/Topic(href, href_list) + if(..()) + return 1 + if( href_list["PC_exit"] ) + kill_program() + return 1 + if( href_list["PC_enable_component"] ) + var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(href_list["PC_enable_component"]) + if(H && istype(H) && !H.enabled) + H.enabled = 1 + . = 1 + if( href_list["PC_disable_component"] ) + var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(href_list["PC_disable_component"]) + if(H && istype(H) && H.enabled) + H.enabled = 0 + . = 1 + if( href_list["PC_shutdown"] ) + shutdown_computer() + return 1 + if( href_list["PC_minimize"] ) + var/mob/user = usr + minimize_program(user) + + if( href_list["PC_killprogram"] ) + var/prog = href_list["PC_killprogram"] + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) + + if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) + return + + P.kill_program(1) + update_uis() + to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") + + if( href_list["PC_runprogram"] ) + return run_program(href_list["PC_runprogram"]) + + if( href_list["PC_setautorun"] ) + if(!hard_drive) + return + set_autorun(href_list["PC_setautorun"]) + + if(.) + update_uis() + +// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" +/obj/item/modular_computer/proc/get_header_data() + var/list/data = list() + + if(battery_module) + switch(battery_module.battery.percent()) + if(80 to 200) // 100 should be maximal but just in case.. + data["PC_batteryicon"] = "batt_100.gif" + if(60 to 80) + data["PC_batteryicon"] = "batt_80.gif" + if(40 to 60) + data["PC_batteryicon"] = "batt_60.gif" + if(20 to 40) + data["PC_batteryicon"] = "batt_40.gif" + if(5 to 20) + data["PC_batteryicon"] = "batt_20.gif" + else + data["PC_batteryicon"] = "batt_5.gif" + data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %" + data["PC_showbatteryicon"] = 1 + else + data["PC_batteryicon"] = "batt_5.gif" + data["PC_batterypercent"] = "N/C" + data["PC_showbatteryicon"] = battery_module ? 1 : 0 + + if(tesla_link && tesla_link.enabled && apc_powered) + data["PC_apclinkicon"] = "charging.gif" + + if(network_card && network_card.is_banned()) + data["PC_ntneticon"] = "sig_warning.gif" + else + switch(get_ntnet_status()) + if(0) + data["PC_ntneticon"] = "sig_none.gif" + if(1) + data["PC_ntneticon"] = "sig_low.gif" + if(2) + data["PC_ntneticon"] = "sig_high.gif" + if(3) + data["PC_ntneticon"] = "sig_lan.gif" + + var/list/program_headers = list() + for(var/datum/computer_file/program/P in idle_threads) + if(!P.ui_header) + continue + program_headers.Add(list(list( + "icon" = P.ui_header + ))) + if(active_program && active_program.ui_header) + program_headers.Add(list(list( + "icon" = active_program.ui_header + ))) + data["PC_programheaders"] = program_headers + + data["PC_stationtime"] = stationtime2text() + data["PC_hasheader"] = 1 + data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen + return data \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/variables.dm b/code/modules/modular_computers/computers/modular_computer/variables.dm new file mode 100644 index 0000000000..c746493ff8 --- /dev/null +++ b/code/modules/modular_computers/computers/modular_computer/variables.dm @@ -0,0 +1,53 @@ +// This is the base type that handles everything. Subtypes can be easily created by tweaking variables in this file to your liking. + +/obj/item/modular_computer + name = "Modular Computer" + desc = "A modular computer. You shouldn't see this." + + var/enabled = 0 // Whether the computer is turned on. + var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. + var/datum/computer_file/program/active_program = null // A currently active program running on the computer. + var/hardware_flag = 0 // A flag that describes this device type + var/last_power_usage = 0 // Last tick power usage of this computer + var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged + var/last_world_time = "00:00" + var/list/last_header_icons + var/computer_emagged = FALSE // Whether the computer is emagged. + var/apc_powered = FALSE // Set automatically. Whether the computer used APC power last tick. + var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too. + var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops) + var/bsod = FALSE // Error screen displayed + + // Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..) + // must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently + // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. + + icon = null // This thing isn't meant to be used on it's own. Subtypes should supply their own icon. + icon_state = null + //center_of_mass = null // No pixelshifting by placing on tables, etc. + //randpixel = 0 // And no random pixelshifting on-creation either. + var/icon_state_unpowered = null // Icon state when the computer is turned off + var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. + var/icon_state_screensaver = null + var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed. + var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer. + var/light_strength = 0 // Intensity of light this computer emits. Comparable to numbers light fixtures use. + var/list/idle_threads = list() // Idle programs on background. They still receive process calls but can't be interacted with. + + // Damage of the chassis. If the chassis takes too much damage it will break apart. + var/damage = 0 // Current damage level + var/broken_damage = 50 // Damage level at which the computer ceases to operate + var/max_damage = 100 // Damage level at which the computer breaks apart. + + // Important hardware (must be installed for computer to work) + var/obj/item/weapon/computer_hardware/processor_unit/processor_unit // CPU. Without it the computer won't run. Better CPUs can run more programs at once. + var/obj/item/weapon/computer_hardware/network_card/network_card // Network Card component of this computer. Allows connection to NTNet + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive // Hard Drive component of this computer. Stores programs and files. + + // Optional hardware (improves functionality, but is not critical for computer to work in most cases) + var/obj/item/weapon/computer_hardware/battery_module/battery_module // An internal power source for this computer. Can be recharged. + var/obj/item/weapon/computer_hardware/card_slot/card_slot // ID Card slot component of this computer. Mostly for HoP modification console that needs ID slot for modification. + var/obj/item/weapon/computer_hardware/nano_printer/nano_printer // Nano Printer component of this computer, for your everyday paperwork needs. + var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive // Portable data storage + var/obj/item/weapon/computer_hardware/ai_slot/ai_slot // AI slot, an intellicard housing that allows modifications of AIs. + var/obj/item/weapon/computer_hardware/tesla_link/tesla_link // Tesla Link, Allows remote charging from nearest APC. \ No newline at end of file diff --git a/code/modules/modular_computers/computers/status_icons.dmi b/code/modules/modular_computers/computers/status_icons.dmi new file mode 100644 index 0000000000..52127ec484 Binary files /dev/null and b/code/modules/modular_computers/computers/status_icons.dmi differ diff --git a/code/modules/modular_computers/computers/subtypes/dev_console.dm b/code/modules/modular_computers/computers/subtypes/dev_console.dm new file mode 100644 index 0000000000..d7f977f33b --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/dev_console.dm @@ -0,0 +1,18 @@ +/obj/item/modular_computer/console + name = "console" + desc = "A stationary computer." + icon = 'icons/obj/modular_console_vr.dmi' //VOREStation Edit + icon_state = "console" + icon_state_unpowered = "console" + icon_state_screensaver = "standby" + icon_state_menu = "menu" + hardware_flag = PROGRAM_CONSOLE + anchored = TRUE + density = 1 + base_idle_power_usage = 100 + base_active_power_usage = 500 + max_hardware_size = 3 + steel_sheet_cost = 20 + light_strength = 4 + max_damage = 300 + broken_damage = 150 \ No newline at end of file diff --git a/code/modules/modular_computers/computers/subtypes/dev_laptop.dm b/code/modules/modular_computers/computers/subtypes/dev_laptop.dm new file mode 100644 index 0000000000..156d67525b --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/dev_laptop.dm @@ -0,0 +1,50 @@ +/obj/item/modular_computer/laptop + anchored = TRUE + name = "laptop computer" + desc = "A portable computer." + hardware_flag = PROGRAM_LAPTOP + icon_state_unpowered = "laptop-open" + icon = 'icons/obj/modular_laptop.dmi' + icon_state = "laptop-open" + icon_state_screensaver = "standby" + base_idle_power_usage = 25 + base_active_power_usage = 200 + max_hardware_size = 2 + light_strength = 3 + max_damage = 200 + broken_damage = 100 + w_class = ITEMSIZE_NORMAL + var/icon_state_closed = "laptop-closed" + +/obj/item/modular_computer/laptop/AltClick(mob/living/carbon/user) + // We need to be close to it to open it + if((!in_range(src, user)) || user.stat || user.restrained()) + return + // Prevents carrying of open laptops inhand. + // While they work inhand, i feel it'd make tablets lose some of their high-mobility advantage they have over laptops now. + if(!istype(loc, /turf/)) + to_chat(usr, "\The [src] has to be on a stable surface first!") + return + //VOREStation Addition Begin + var/supported = FALSE + for(var/obj/structure/table/S in loc) + supported = TRUE + if(!supported && !anchored) + to_chat(usr, "You will need a better supporting surface before opening \the [src]!") + return + //VOREStation Addition End + anchored = !anchored + screen_on = anchored + update_icon() + +/obj/item/modular_computer/laptop/update_icon() + if(anchored) + ..() + else + overlays.Cut() + set_light(0) // No glow from closed laptops + icon_state = icon_state_closed + +/obj/item/modular_computer/laptop/preset + anchored = FALSE + screen_on = FALSE diff --git a/code/modules/modular_computers/computers/subtypes/dev_tablet.dm b/code/modules/modular_computers/computers/subtypes/dev_tablet.dm new file mode 100644 index 0000000000..62d12ba545 --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/dev_tablet.dm @@ -0,0 +1,16 @@ +/obj/item/modular_computer/tablet + name = "tablet computer" + desc = "A small portable microcomputer" + icon = 'icons/obj/modular_tablet.dmi' + icon_state = "tablet" + icon_state_unpowered = "tablet" + icon_state_menu = "menu" + hardware_flag = PROGRAM_TABLET + max_hardware_size = 1 + w_class = ITEMSIZE_SMALL + light_strength = 2 // Same as PDAs + +/obj/item/modular_computer/tablet/lease + desc = "A small portable microcomputer. This one has a gold and blue stripe, and a serial number stamped into the case." + icon_state = "tabletsol" + icon_state_unpowered = "tabletsol" \ No newline at end of file diff --git a/code/modules/modular_computers/computers/subtypes/dev_telescreen.dm b/code/modules/modular_computers/computers/subtypes/dev_telescreen.dm new file mode 100644 index 0000000000..1f88ed6cb2 --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/dev_telescreen.dm @@ -0,0 +1,59 @@ +/obj/item/modular_computer/telescreen + name = "telescreen" + desc = "A wall-mounted touchscreen computer." + icon = 'icons/obj/modular_telescreen.dmi' + icon_state = "telescreen" + icon_state_unpowered = "telescreen" + icon_state_menu = "menu" + icon_state_screensaver = "standby" + hardware_flag = PROGRAM_TELESCREEN + anchored = TRUE + density = 0 + base_idle_power_usage = 75 + base_active_power_usage = 300 + max_hardware_size = 2 + steel_sheet_cost = 10 + light_strength = 4 + max_damage = 300 + broken_damage = 150 + w_class = ITEMSIZE_HUGE + +/obj/item/modular_computer/telescreen/New() + ..() + // Allows us to create "north bump" "south bump" etc. named objects, for more comfortable mapping. + name = "telescreen" + +/obj/item/modular_computer/telescreen/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.is_crowbar()) + if(anchored) + shutdown_computer() + anchored = FALSE + screen_on = FALSE + pixel_x = 0 + pixel_y = 0 + to_chat(user, "You unsecure \the [src].") + else + var/choice = input(user, "Where do you want to place \the [src]?", "Offset selection") in list("North", "South", "West", "East", "This tile", "Cancel") + var/valid = FALSE + switch(choice) + if("North") + valid = TRUE + pixel_y = 32 + if("South") + valid = TRUE + pixel_y = -32 + if("West") + valid = TRUE + pixel_x = -32 + if("East") + valid = TRUE + pixel_x = 32 + if("This tile") + valid = TRUE + + if(valid) + anchored = 1 + screen_on = TRUE + to_chat(user, "You secure \the [src].") + return + ..() \ No newline at end of file diff --git a/code/modules/modular_computers/computers/subtypes/preset_console.dm b/code/modules/modular_computers/computers/subtypes/preset_console.dm new file mode 100644 index 0000000000..d2f409b51e --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/preset_console.dm @@ -0,0 +1,123 @@ +/obj/item/modular_computer/console/preset/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/wired(src) + +// Engineering +/obj/item/modular_computer/console/preset/engineering/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/power_monitor()) + hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor()) + hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) + hard_drive.store_file(new/datum/computer_file/program/atmos_control()) + hard_drive.store_file(new/datum/computer_file/program/rcon_console()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + +// Medical +/obj/item/modular_computer/console/preset/medical/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/suit_sensors()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + hard_drive.store_file(new/datum/computer_file/program/wordprocessor()) + set_autorun("sensormonitor") + +// Research +/obj/item/modular_computer/console/preset/research/install_default_hardware() + ..() + //ai_slot = new/obj/item/weapon/computer_hardware/ai_slot(src) + +/obj/item/modular_computer/console/preset/research/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor()) + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + //hard_drive.store_file(new/datum/computer_file/program/aidiag()) + hard_drive.store_file(new/datum/computer_file/program/email_client()) + hard_drive.store_file(new/datum/computer_file/program/wordprocessor()) + +// Administrator +/obj/item/modular_computer/console/preset/sysadmin/install_default_hardware() + ..() + //ai_slot = new/obj/item/weapon/computer_hardware/ai_slot(src) + +/obj/item/modular_computer/console/preset/sysadmin/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor()) + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + //hard_drive.store_file(new/datum/computer_file/program/aidiag()) + hard_drive.store_file(new/datum/computer_file/program/email_client()) + hard_drive.store_file(new/datum/computer_file/program/email_administration()) + hard_drive.store_file(new/datum/computer_file/program/wordprocessor()) + +// Command +/obj/item/modular_computer/console/preset/command/install_default_hardware() + ..() + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + +/obj/item/modular_computer/console/preset/command/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/card_mod()) + hard_drive.store_file(new/datum/computer_file/program/comm()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + hard_drive.store_file(new/datum/computer_file/program/email_client()) + +// Security +/obj/item/modular_computer/console/preset/security/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + hard_drive.store_file(new/datum/computer_file/program/digitalwarrant()) + hard_drive.store_file(new/datum/computer_file/program/wordprocessor()) + +// Civilian +/obj/item/modular_computer/console/preset/civilian/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) + hard_drive.store_file(new/datum/computer_file/program/newsbrowser()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + hard_drive.store_file(new/datum/computer_file/program/email_client()) + hard_drive.store_file(new/datum/computer_file/program/wordprocessor()) + +// ERT +/obj/item/modular_computer/console/preset/ert/install_default_hardware() + ..() + //ai_slot = new/obj/item/weapon/computer_hardware/ai_slot(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + +/obj/item/modular_computer/console/preset/ert/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor/ert()) + hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) + hard_drive.store_file(new/datum/computer_file/program/comm()) + //hard_drive.store_file(new/datum/computer_file/program/aidiag()) + +// Mercenary +/obj/item/modular_computer/console/preset/mercenary/ + computer_emagged = TRUE + +/obj/item/modular_computer/console/preset/mercenary/install_default_hardware() + ..() + //ai_slot = new/obj/item/weapon/computer_hardware/ai_slot(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + +/obj/item/modular_computer/console/preset/mercenary/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/camera_monitor/hacked()) + hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) + //hard_drive.store_file(new/datum/computer_file/program/aidiag()) + +// Merchant +/obj/item/modular_computer/console/preset/merchant/install_default_programs() + ..() + //hard_drive.store_file(new/datum/computer_file/program/merchant()) + hard_drive.store_file(new/datum/computer_file/program/wordprocessor()) diff --git a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm new file mode 100644 index 0000000000..905df24590 --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm @@ -0,0 +1,68 @@ +/obj/item/modular_computer/laptop/preset/custom_loadout/cheap/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/laptop/preset/custom_loadout/advanced/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/laptop/preset/custom_loadout/standard/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/laptop/preset/custom_loadout/elite + icon_state_unpowered = "adv-laptop-open" + icon_state = "adv-laptop-open" + icon_state_closed = "adv-laptop-closed" + +/obj/item/modular_computer/laptop/preset/custom_loadout/elite/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src) + battery_module.charge_to_full() + +//VOREStation Add Start +/obj/item/modular_computer/laptop/preset/custom_loadout/rugged + name = "rugged laptop computer" + desc = "A rugged portable computer." + icon = 'icons/obj/modular_laptop_vr.dmi' + max_damage = 300 + broken_damage = 200 + +/obj/item/modular_computer/laptop/preset/custom_loadout/rugged/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src) + battery_module.charge_to_full() +//VOREStation Add End \ No newline at end of file diff --git a/code/modules/modular_computers/computers/subtypes/preset_tablet.dm b/code/modules/modular_computers/computers/subtypes/preset_tablet.dm new file mode 100644 index 0000000000..65c3a47c3c --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/preset_tablet.dm @@ -0,0 +1,28 @@ +/obj/item/modular_computer/tablet/preset/custom_loadout/cheap/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/micro(src) + network_card = new/obj/item/weapon/computer_hardware/network_card(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/nano(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/tablet/preset/custom_loadout/advanced/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/tablet/preset/custom_loadout/standard/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src) + network_card = new/obj/item/weapon/computer_hardware/network_card(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/micro(src) + battery_module.charge_to_full() diff --git a/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm b/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm new file mode 100644 index 0000000000..d0cb86bada --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/preset_telescreen.dm @@ -0,0 +1,14 @@ +/obj/item/modular_computer/telescreen/preset/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive(src) + network_card = new/obj/item/weapon/computer_hardware/network_card(src) + +/obj/item/modular_computer/telescreen/preset/generic/install_default_programs() + ..() + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) + hard_drive.store_file(new/datum/computer_file/program/camera_monitor()) + hard_drive.store_file(new/datum/computer_file/program/email_client()) + set_autorun("cammon") diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm new file mode 100644 index 0000000000..afc02e8414 --- /dev/null +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -0,0 +1,39 @@ +var/global/file_uid = 0 + +/datum/computer_file/ + var/filename = "NewFile" // Placeholder. No spacebars + var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case + var/size = 1 // File size in GQ. Integers only! + var/obj/item/weapon/computer_hardware/hard_drive/holder // Holder that contains this file. + var/unsendable = 0 // Whether the file may be sent to someone via NTNet transfer or other means. + var/undeletable = 0 // Whether the file may be deleted. Setting to 1 prevents deletion/renaming/etc. + var/uid // UID of this file + +/datum/computer_file/New() + ..() + uid = file_uid + file_uid++ + +/datum/computer_file/Destroy() + if(!holder) + return ..() + + holder.remove_file(src) + // holder.holder is the computer that has drive installed. If we are Destroy()ing program that's currently running kill it. + if(holder.holder2 && holder.holder2.active_program == src) + holder.holder2.kill_program(1) + holder = null + ..() + +// Returns independent copy of this file. +/datum/computer_file/proc/clone(var/rename = 0) + var/datum/computer_file/temp = new type + temp.unsendable = unsendable + temp.undeletable = undeletable + temp.size = size + if(rename) + temp.filename = filename + "(Copy)" + else + temp.filename = filename + temp.filetype = filetype + return temp \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/data.dm b/code/modules/modular_computers/file_system/data.dm new file mode 100644 index 0000000000..7ad48bea54 --- /dev/null +++ b/code/modules/modular_computers/file_system/data.dm @@ -0,0 +1,19 @@ +// /data/ files store data in string format. +// They don't contain other logic for now. +/datum/computer_file/data + var/stored_data = "" // Stored data in string format. + filetype = "DAT" + var/block_size = 250 + var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited. + +/datum/computer_file/data/clone() + var/datum/computer_file/data/temp = ..() + temp.stored_data = stored_data + return temp + +// Calculates file size from amount of characters in saved string +/datum/computer_file/data/proc/calculate_size() + size = max(1, round(length(stored_data) / block_size)) + +/datum/computer_file/data/logfile + filetype = "LOG" diff --git a/code/modules/modular_computers/file_system/news_article.dm b/code/modules/modular_computers/file_system/news_article.dm new file mode 100644 index 0000000000..833ba382e9 --- /dev/null +++ b/code/modules/modular_computers/file_system/news_article.dm @@ -0,0 +1,24 @@ +// /data/ files store data in string format. +// They don't contain other logic for now. +/datum/computer_file/data/news_article + filetype = "XNML" + filename = "Unknown News Entry" + block_size = 5000 // Results in smaller files + do_not_edit = 1 // Editing the file breaks most formatting due to some HTML tags not being accepted as input from average user. + var/server_file_path // File path to HTML file that will be loaded on server start. Example: '/news_articles/space_magazine_1.html'. Use the /news_articles/ folder! + var/archived // Set to 1 for older stuff + var/cover //filename of cover. + +/datum/computer_file/data/news_article/New(var/load_from_file = 0) + ..() + if(server_file_path && load_from_file) + stored_data = file2text(server_file_path) + calculate_size() + + +// NEWS DEFINITIONS BELOW THIS LINE +/* KEPT HERE AS AN EXAMPLE +/datum/computer_file/data/news_article/space/vol_one + filename = "SPACE Magazine vol. 1" + server_file_path = 'news_articles/space_magazine_1.html' +*/ \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm new file mode 100644 index 0000000000..7124bdcd03 --- /dev/null +++ b/code/modules/modular_computers/file_system/program.dm @@ -0,0 +1,210 @@ +// /program/ files are executable programs that do things. +/datum/computer_file/program + filetype = "PRG" + filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET! + var/required_access = null // List of required accesses to run/download the program. + var/requires_access_to_run = 1 // Whether the program checks for required_access when run. + var/requires_access_to_download = 1 // Whether the program checks for required_access when downloading. + var/datum/nano_module/NM = null // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact. + var/nanomodule_path = null // Path to nanomodule, make sure to set this if implementing new program. + var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. + var/obj/item/modular_computer/computer // Device that runs this program. + var/filedesc = "Unknown Program" // User-friendly name of this program. + var/extended_desc = "N/A" // Short description of this program's function. + var/program_icon_state = null // Program-specific screen icon state + var/program_key_state = "standby_key" // Program-specific keyboard icon state + var/program_menu_icon = "newwin" // Icon to use for program's link in main menu + var/requires_ntnet = 0 // Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes. + var/requires_ntnet_feature = 0 // Optional, if above is set to 1 checks for specific function of NTNet (currently NTNET_SOFTWAREDOWNLOAD, NTNET_PEERTOPEER, NTNET_SYSTEMCONTROL and NTNET_COMMUNICATION) + var/ntnet_status = 1 // NTNet status, updated every tick by computer running this program. Don't use this for checks if NTNet works, computers do that. Use this for calculations, etc. + var/usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL + var/network_destination = null // Optional string that describes what NTNet server/system this program connects to. Used in default logging. + var/available_on_ntnet = 1 // Whether the program can be downloaded from NTNet. Set to 0 to disable. + var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. + var/computer_emagged = 0 // Set to 1 if computer that's running us was emagged. Computer updates this every Process() tick + var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /nano/images/status_icons. Be careful not to use too large images! + var/ntnet_speed = 0 // GQ/s - current network connectivity transfer rate + +/datum/computer_file/program/New(var/obj/item/modular_computer/comp = null) + ..() + if(comp && istype(comp)) + computer = comp + +/datum/computer_file/program/Destroy() + computer = null + . = ..() + +/datum/computer_file/program/nano_host() + return computer.nano_host() + +/datum/computer_file/program/clone() + var/datum/computer_file/program/temp = ..() + temp.required_access = required_access + temp.nanomodule_path = nanomodule_path + temp.filedesc = filedesc + temp.program_icon_state = program_icon_state + temp.requires_ntnet = requires_ntnet + temp.requires_ntnet_feature = requires_ntnet_feature + temp.usage_flags = usage_flags + return temp + +// Relays icon update to the computer. +/datum/computer_file/program/proc/update_computer_icon() + if(computer) + computer.update_icon() + +// Attempts to create a log in global ntnet datum. Returns 1 on success, 0 on fail. +/datum/computer_file/program/proc/generate_network_log(var/text) + if(computer) + return computer.add_log(text) + return 0 + +/datum/computer_file/program/proc/is_supported_by_hardware(var/hardware_flag = 0, var/loud = 0, var/mob/user = null) + if(!(hardware_flag & usage_flags)) + if(loud && computer && user) + to_chat(user, "\The [computer] flashes: \"Hardware Error - Incompatible software\".") + return 0 + return 1 + +/datum/computer_file/program/proc/get_signal(var/specific_action = 0) + if(computer) + return computer.get_ntnet_status(specific_action) + return 0 + +// Called by Process() on device that runs us, once every tick. +/datum/computer_file/program/proc/process_tick() + update_netspeed() + return 1 + +/datum/computer_file/program/proc/update_netspeed() + ntnet_speed = 0 + switch(ntnet_status) + if(1) + ntnet_speed = NTNETSPEED_LOWSIGNAL + if(2) + ntnet_speed = NTNETSPEED_HIGHSIGNAL + if(3) + ntnet_speed = NTNETSPEED_ETHERNET + +// Check if the user can run program. Only humans can operate computer. Automatically called in run_program() +// User has to wear their ID or have it inhand for ID Scan to work. +// Can also be called manually, with optional parameter being access_to_check to scan the user's ID +/datum/computer_file/program/proc/can_run(var/mob/living/user, var/loud = 0, var/access_to_check) + // Defaults to required_access + if(!access_to_check) + access_to_check = required_access + if(!access_to_check) // No required_access, allow it. + return 1 + + // Admin override - allows operation of any computer as aghosted admin, as if you had any required access. + if(istype(user, /mob/observer/dead) && check_rights(R_ADMIN, 0, user)) + return 1 + + if(!istype(user)) + return 0 + + var/obj/item/weapon/card/id/I = user.GetIdCard() + if(!I) + if(loud) + to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.") + return 0 + + if(access_to_check in I.access) + return 1 + else if(loud) + to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") + +// This attempts to retrieve header data for NanoUIs. If implementing completely new device of different type than existing ones +// always include the device here in this proc. This proc basically relays the request to whatever is running the program. +/datum/computer_file/program/proc/get_header_data() + if(computer) + return computer.get_header_data() + return list() + +// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure. +// When implementing new program based device, use this to run the program. +/datum/computer_file/program/proc/run_program(var/mob/living/user) + if(can_run(user, 1) || !requires_access_to_run) + if(nanomodule_path) + NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src) + NM.using_access = user.GetAccess() + if(requires_ntnet && network_destination) + generate_network_log("Connection opened to [network_destination].") + program_state = PROGRAM_STATE_ACTIVE + return 1 + return 0 + +// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. +/datum/computer_file/program/proc/kill_program(var/forced = 0) + program_state = PROGRAM_STATE_KILLED + if(network_destination) + generate_network_log("Connection to [network_destination] closed.") + if(NM) + qdel(NM) + NM = null + return 1 + +// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation. +// It returns 0 if it can't run or if NanoModule was used instead. I suggest using NanoModules where applicable. +/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. + if(ui) + ui.close() + return computer.ui_interact(user) + if(istype(NM)) + NM.ui_interact(user, ui_key, null, force_open) + return 0 + return 1 + + +// CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: +// Topic calls are automagically forwarded from NanoModule this program contains. +// Calls beginning with "PRG_" are reserved for programs handling. +// Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program) +// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. +/datum/computer_file/program/Topic(href, href_list) + if(..()) + return 1 + if(computer) + return computer.Topic(href, href_list) + +// Relays the call to nano module, if we have one +/datum/computer_file/program/proc/check_eye(var/mob/user) + if(NM) + return NM.check_eye(user) + else + return -1 + +/obj/item/modular_computer/initial_data() + return get_header_data() + +/obj/item/modular_computer/update_layout() + return TRUE + +/datum/nano_module/program + //available_to_ai = FALSE + var/datum/computer_file/program/program = null // Program-Based computer program that runs this nano module. Defaults to null. + +/datum/nano_module/program/New(var/host, var/topic_manager, var/program) + ..() + src.program = program + +/datum/topic_manager/program + var/datum/program + +/datum/topic_manager/program/New(var/datum/program) + ..() + src.program = program + +// Calls forwarded to PROGRAM itself should begin with "PRG_" +// Calls forwarded to COMPUTER running the program should begin with "PC_" +/datum/topic_manager/program/Topic(href, href_list) + return program && program.Topic(href, href_list) + +/datum/computer_file/program/apply_visual(mob/M) + if(NM) + NM.apply_visual(M) + +/datum/computer_file/program/remove_visual(mob/M) + if(NM) + NM.remove_visual(M) diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm new file mode 100644 index 0000000000..ef1eac09aa --- /dev/null +++ b/code/modules/modular_computers/file_system/program_events.dm @@ -0,0 +1,18 @@ +// Events are sent to the program by the computer. +// Always include a parent call when overriding an event. + +// Called when the ID card is removed from computer. ID is removed AFTER this proc. +/datum/computer_file/program/proc/event_idremoved(var/background) + return + +// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss. +/datum/computer_file/program/proc/event_powerfailure(var/background) + kill_program(1) + +// Called when the network connectivity fails. Computer does necessary checks and only calls this when requires_ntnet_feature and similar variables are not met. +/datum/computer_file/program/proc/event_networkfailure(var/background) + kill_program(1) + if(background) + computer.visible_message("\The [computer]'s screen displays an error: \"Network connectivity lost - process [filename].[filetype] (PID [rand(100,999)]) terminated.\"") + else + computer.visible_message("\The [computer]'s screen briefly freezes and then shows: \"FATAL NETWORK ERROR - NTNet connection lost. Please try again later. If problem persists, please contact your system administrator.\"") diff --git a/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm b/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm new file mode 100644 index 0000000000..04bf931593 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm @@ -0,0 +1,129 @@ +/datum/computer_file/program/access_decrypter + filename = "nt_accrypt" + filedesc = "NTNet Access Decrypter" + program_icon_state = "hostile" + program_key_state = "security_key" + program_menu_icon = "unlocked" + extended_desc = "This highly advanced script can very slowly decrypt operational codes used in almost any network. These codes can be downloaded to an ID card to expand the available access. The system administrator will probably notice this." + size = 34 + requires_ntnet = 1 + available_on_ntnet = 0 + available_on_syndinet = 1 + nanomodule_path = /datum/nano_module/program/access_decrypter/ + var/message = "" + var/running = FALSE + var/progress = 0 + var/target_progress = 300 + var/datum/access/target_access = null + +/datum/computer_file/program/access_decrypter/kill_program(var/forced) + reset() + ..(forced) + +/datum/computer_file/program/access_decrypter/proc/reset() + running = FALSE + message = "" + progress = 0 + +/datum/computer_file/program/access_decrypter/process_tick() + . = ..() + if(!running) + return + var/obj/item/weapon/computer_hardware/processor_unit/CPU = computer.processor_unit + var/obj/item/weapon/computer_hardware/card_slot/RFID = computer.card_slot + if(!istype(CPU) || !CPU.check_functionality() || !istype(RFID) || !RFID.check_functionality()) + message = "A fatal hardware error has been detected." + return + if(!istype(RFID.stored_card)) + message = "RFID card has been removed from the device. Operation aborted." + return + + progress += CPU.max_idle_programs + if(progress >= target_progress) + reset() + RFID.stored_card.access |= target_access.id + if(ntnet_global.intrusion_detection_enabled) + ntnet_global.add_log("IDS WARNING - Unauthorised access to primary keycode database from device: [computer.network_card.get_network_tag()] - downloaded access codes for: [target_access.desc].") + ntnet_global.intrusion_detection_alarm = 1 + message = "Successfully decrypted and saved operational key codes. Downloaded access codes for: [target_access.desc]" + target_access = null + +/datum/computer_file/program/access_decrypter/Topic(href, href_list) + if(..()) + return 1 + if(href_list["PRG_reset"]) + reset() + return 1 + if(href_list["PRG_execute"]) + if(running) + return 1 + if(text2num(href_list["allowed"])) + return 1 + var/obj/item/weapon/computer_hardware/processor_unit/CPU = computer.processor_unit + var/obj/item/weapon/computer_hardware/card_slot/RFID = computer.card_slot + if(!istype(CPU) || !CPU.check_functionality() || !istype(RFID) || !RFID.check_functionality()) + message = "A fatal hardware error has been detected." + return + if(!istype(RFID.stored_card)) + message = "RFID card is not present in the device. Operation aborted." + return + running = TRUE + target_access = get_access_by_id(href_list["PRG_execute"]) + if(ntnet_global.intrusion_detection_enabled) + ntnet_global.add_log("IDS WARNING - Unauthorised access attempt to primary keycode database from device: [computer.network_card.get_network_tag()]") + ntnet_global.intrusion_detection_alarm = 1 + return 1 + +/datum/nano_module/program/access_decrypter + name = "NTNet Access Decrypter" + var/list/restricted_access_codes = list(access_change_ids, access_network) // access codes that are not hackable due to balance reasons + +/datum/nano_module/program/access_decrypter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!ntnet_global) + return + var/datum/computer_file/program/access_decrypter/PRG = program + var/list/data = list() + if(!istype(PRG)) + return + data = PRG.get_header_data() + + if(PRG.message) + data["message"] = PRG.message + else if(PRG.running) + data["running"] = 1 + data["rate"] = PRG.computer.processor_unit.max_idle_programs + + // Stolen from DOS traffic generator, generates strings of 1s and 0s + var/percentage = (PRG.progress / PRG.target_progress) * 100 + var/list/strings[0] + for(var/j, j<10, j++) + var/string = "" + for(var/i, i<20, i++) + string = "[string][prob(percentage)]" + strings.Add(string) + data["dos_strings"] = strings + else if(program.computer.card_slot && program.computer.card_slot.stored_card) + var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card + var/list/regions = list() + for(var/i = 1; i <= 7; i++) + var/list/accesses = list() + for(var/access in get_region_accesses(i)) + if (get_access_desc(access)) + accesses.Add(list(list( + "desc" = replacetext(get_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in id_card.access) ? 1 : 0, + "blocked" = (access in restricted_access_codes) ? 1 : 0))) + + regions.Add(list(list( + "name" = get_region_accesses_name(i), + "accesses" = accesses))) + data["regions"] = regions + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "access_decrypter.tmpl", "NTNet Access Decrypter", 550, 400, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm new file mode 100644 index 0000000000..0e0a1381bd --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -0,0 +1,108 @@ +/datum/computer_file/program/ntnet_dos + filename = "ntn_dos" + filedesc = "DoS Traffic Generator" + program_icon_state = "hostile" + program_key_state = "security_key" + program_menu_icon = "arrow-4-diag" + extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect" + size = 20 + requires_ntnet = 1 + available_on_ntnet = 0 + available_on_syndinet = 1 + nanomodule_path = /datum/nano_module/program/computer_dos/ + var/obj/machinery/ntnet_relay/target = null + var/dos_speed = 0 + var/error = "" + var/executed = 0 + +/datum/computer_file/program/ntnet_dos/process_tick() + dos_speed = 0 + switch(ntnet_status) + if(1) + dos_speed = NTNETSPEED_LOWSIGNAL * NTNETSPEED_DOS_AMPLIFICATION + if(2) + dos_speed = NTNETSPEED_HIGHSIGNAL * NTNETSPEED_DOS_AMPLIFICATION + if(3) + dos_speed = NTNETSPEED_ETHERNET * NTNETSPEED_DOS_AMPLIFICATION + if(target && executed) + target.dos_overload += dos_speed + if(!target.operable()) + target.dos_sources.Remove(src) + target = null + error = "Connection to destination relay lost." + +/datum/computer_file/program/ntnet_dos/kill_program(var/forced) + if(target) + target.dos_sources.Remove(src) + target = null + executed = 0 + + ..(forced) + +/datum/nano_module/program/computer_dos + name = "DoS Traffic Generator" + +/datum/nano_module/program/computer_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!ntnet_global) + return + var/datum/computer_file/program/ntnet_dos/PRG = program + var/list/data = list() + if(!istype(PRG)) + return + data = PRG.get_header_data() + + if(PRG.error) + data["error"] = PRG.error + else if(PRG.target && PRG.executed) + data["target"] = 1 + data["speed"] = PRG.dos_speed + + // This is mostly visual, generate some strings of 1s and 0s + // Probability of 1 is equal of completion percentage of DoS attack on this relay. + // Combined with UI updates this adds quite nice effect to the UI + var/percentage = PRG.target.dos_overload * 100 / PRG.target.dos_capacity + var/list/strings[0] + for(var/j, j<10, j++) + var/string = "" + for(var/i, i<20, i++) + string = "[string][prob(percentage)]" + strings.Add(string) + data["dos_strings"] = strings + else + var/list/relays[0] + for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + relays.Add(R.uid) + data["relays"] = relays + data["focus"] = PRG.target ? PRG.target.uid : null + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 400, 250, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/computer_file/program/ntnet_dos/Topic(href, href_list) + if(..()) + return 1 + if(href_list["PRG_target_relay"]) + for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + if("[R.uid]" == href_list["PRG_target_relay"]) + target = R + return 1 + if(href_list["PRG_reset"]) + if(target) + target.dos_sources.Remove(src) + target = null + executed = 0 + error = "" + return 1 + if(href_list["PRG_execute"]) + if(target) + executed = 1 + target.dos_sources.Add(src) + if(ntnet_global.intrusion_detection_enabled) + ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [computer.network_card.get_network_tag()]") + ntnet_global.intrusion_detection_alarm = 1 + return 1 diff --git a/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm b/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm new file mode 100644 index 0000000000..27e5a26c3f --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm @@ -0,0 +1,39 @@ +/datum/computer_file/program/camera_monitor/hacked + filename = "camcrypt" + filedesc = "Camera Decryption Tool" + nanomodule_path = /datum/nano_module/camera_monitor/hacked + program_icon_state = "hostile" + program_key_state = "security_key" + program_menu_icon = "zoomin" + extended_desc = "This very advanced piece of software uses adaptive programming and large database of cipherkeys to bypass most encryptions used on camera networks. Be warned that system administrator may notice this." + size = 73 // Very large, a price for bypassing ID checks completely. + available_on_ntnet = 0 + available_on_syndinet = 1 + +/datum/computer_file/program/camera_monitor/hacked/process_tick() + ..() + if(program_state != PROGRAM_STATE_ACTIVE) // Background programs won't trigger alarms. + return + + var/datum/nano_module/camera_monitor/hacked/HNM = NM + + // The program is active and connected to one of the station's networks. Has a very small chance to trigger IDS alarm every tick. + if(HNM && HNM.current_network && (HNM.current_network in using_map.station_networks) && prob(0.1)) + if(ntnet_global.intrusion_detection_enabled) + ntnet_global.add_log("IDS WARNING - Unauthorised access detected to camera network [HNM.current_network] by device with NID [computer.network_card.get_network_tag()]") + ntnet_global.intrusion_detection_alarm = 1 + + +/datum/nano_module/camera_monitor/hacked + name = "Hacked Camera Monitoring Program" + //available_to_ai = FALSE + +/datum/nano_module/camera_monitor/hacked/can_access_network(var/mob/user, var/network_access) + return 1 + +// The hacked variant has access to all commonly used networks. +/datum/nano_module/camera_monitor/hacked/modify_networks_list(var/list/networks) + networks.Add(list(list("tag" = NETWORK_MERCENARY, "has_access" = 1))) + networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1))) + networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1))) + return networks \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm new file mode 100644 index 0000000000..17199c3b18 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -0,0 +1,84 @@ +/datum/computer_file/program/revelation + filename = "revelation" + filedesc = "Revelation" + program_icon_state = "hostile" + program_key_state = "security_key" + program_menu_icon = "home" + extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution." + size = 13 + requires_ntnet = 0 + available_on_ntnet = 0 + available_on_syndinet = 1 + nanomodule_path = /datum/nano_module/program/revelation/ + var/armed = 0 + +/datum/computer_file/program/revelation/run_program(var/mob/living/user) + . = ..(user) + if(armed) + activate() + +/datum/computer_file/program/revelation/proc/activate() + if(!computer) + return + + computer.visible_message("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.") + computer.enabled = 0 + computer.update_icon() + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(10, 1, computer.loc) + s.start() + + if(computer.hard_drive) + qdel(computer.hard_drive) + + if(computer.battery_module && prob(25)) + qdel(computer.battery_module) + + if(computer.tesla_link && prob(50)) + qdel(computer.tesla_link) + +/datum/computer_file/program/revelation/Topic(href, href_list) + if(..()) + return 1 + else if(href_list["PRG_arm"]) + armed = !armed + else if(href_list["PRG_activate"]) + activate() + else if(href_list["PRG_obfuscate"]) + var/mob/living/user = usr + var/newname = sanitize(input(user, "Enter new program name: ")) + if(!newname) + return + filedesc = newname + for(var/datum/computer_file/program/P in ntnet_global.available_station_software) + if(filedesc == P.filedesc) + program_menu_icon = P.program_menu_icon + break + return 1 + +/datum/computer_file/program/revelation/clone() + var/datum/computer_file/program/revelation/temp = ..() + temp.armed = armed + return temp + +/datum/nano_module/program/revelation + name = "Revelation Virus" + +/datum/nano_module/program/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = list() + var/datum/computer_file/program/revelation/PRG = program + if(!istype(PRG)) + return + + data = PRG.get_header_data() + + data["armed"] = PRG.armed + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "revelation.tmpl", "Revelation Virus", 400, 250, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm new file mode 100644 index 0000000000..5bcac7adf8 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -0,0 +1,226 @@ +/datum/computer_file/program/card_mod + filename = "cardmod" + filedesc = "ID card modification program" + nanomodule_path = /datum/nano_module/program/card_mod + program_icon_state = "id" + program_key_state = "id_key" + program_menu_icon = "key" + extended_desc = "Program for programming crew ID cards." + required_access = access_change_ids + requires_ntnet = 0 + size = 8 + +/datum/nano_module/program/card_mod + name = "ID card modification program" + var/mod_mode = 1 + var/is_centcom = 0 + var/show_assignments = 0 + +/datum/nano_module/program/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + data["src"] = "\ref[src]" + data["station_name"] = station_name() + data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["assignments"] = show_assignments + if(program && program.computer) + data["have_id_slot"] = !!program.computer.card_slot + data["have_printer"] = !!program.computer.nano_printer + data["authenticated"] = program.can_run(user) + if(!program.computer.card_slot) + mod_mode = 0 //We can't modify IDs when there is no card reader + else + data["have_id_slot"] = 0 + data["have_printer"] = 0 + data["authenticated"] = 0 + data["mmode"] = mod_mode + data["centcom_access"] = is_centcom + + if(program && program.computer && program.computer.card_slot) + var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card + data["has_id"] = !!id_card + data["id_account_number"] = id_card ? id_card.associated_account_number : null + data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned" + data["id_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----" + data["id_name"] = id_card ? id_card.name : "-----" + + data["command_jobs"] = format_jobs(command_positions) + data["engineering_jobs"] = format_jobs(engineering_positions) + data["medical_jobs"] = format_jobs(medical_positions) + data["science_jobs"] = format_jobs(science_positions) + data["security_jobs"] = format_jobs(security_positions) + data["cargo_jobs"] = format_jobs(cargo_positions) + data["civilian_jobs"] = format_jobs(civilian_positions) + data["centcom_jobs"] = format_jobs(get_all_centcom_jobs()) + + data["all_centcom_access"] = is_centcom ? get_accesses(1) : null + data["regions"] = get_accesses() + + if(program.computer.card_slot && program.computer.card_slot.stored_card) + var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card + if(is_centcom) + var/list/all_centcom_access = list() + for(var/access in get_all_centcom_access()) + all_centcom_access.Add(list(list( + "desc" = replacetext(get_centcom_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in id_card.access) ? 1 : 0))) + data["all_centcom_access"] = all_centcom_access + else + var/list/regions = list() + for(var/i = 1; i <= 7; i++) + var/list/accesses = list() + for(var/access in get_region_accesses(i)) + if (get_access_desc(access)) + accesses.Add(list(list( + "desc" = replacetext(get_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in id_card.access) ? 1 : 0))) + + regions.Add(list(list( + "name" = get_region_accesses_name(i), + "accesses" = accesses))) + data["regions"] = regions + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "mod_identification_computer.tmpl", name, 600, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + +/datum/nano_module/program/card_mod/proc/format_jobs(list/jobs) + var/obj/item/weapon/card/id/id_card = program.computer.card_slot ? program.computer.card_slot.stored_card : null + var/list/formatted = list() + for(var/job in jobs) + formatted.Add(list(list( + "display_name" = replacetext(job, " ", " "), + "target_rank" = id_card && id_card.assignment ? id_card.assignment : "Unassigned", + "job" = job))) + + return formatted + +/datum/nano_module/program/card_mod/proc/get_accesses(var/is_centcom = 0) + return null + + +/datum/computer_file/program/card_mod/Topic(href, href_list) + if(..()) + return 1 + + var/mob/user = usr + var/obj/item/weapon/card/id/user_id_card = user.GetIdCard() + var/obj/item/weapon/card/id/id_card + if (computer.card_slot) + id_card = computer.card_slot.stored_card + + var/datum/nano_module/program/card_mod/module = NM + switch(href_list["action"]) + if("switchm") + if(href_list["target"] == "mod") + module.mod_mode = 1 + else if (href_list["target"] == "manifest") + module.mod_mode = 0 + if("togglea") + if(module.show_assignments) + module.show_assignments = 0 + else + module.show_assignments = 1 + if("print") + if(computer && computer.nano_printer) //This option should never be called if there is no printer + if(module.mod_mode) + if(can_run(user, 1)) + var/contents = {"

      Access Report

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

      + Access:
      + "} + + var/known_access_rights = get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM) + for(var/A in id_card.access) + if(A in known_access_rights) + contents += " [get_access_desc(A)]" + + if(!computer.nano_printer.print_text(contents,"access report")) + to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + return + else + computer.visible_message("\The [computer] prints out paper.") + else + var/contents = {"

      Crew Manifest

      +
      + [data_core ? data_core.get_manifest(0) : ""] + "} + if(!computer.nano_printer.print_text(contents,text("crew manifest ([])", stationtime2text()))) + to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + return + else + computer.visible_message("\The [computer] prints out paper.") + if("eject") + if(computer && computer.card_slot) + if(id_card) + data_core.manifest_modify(id_card.registered_name, id_card.assignment) + computer.proc_eject_id(user) + if("terminate") + if(computer && can_run(user, 1)) + id_card.assignment = "Dismissed" //VOREStation Edit: setting adjustment + id_card.access = list() + callHook("terminate_employee", list(id_card)) + if("edit") + if(computer && can_run(user, 1)) + if(href_list["name"]) + var/temp_name = sanitizeName(input("Enter name.", "Name", id_card.registered_name),allow_numbers=TRUE) + if(temp_name) + id_card.registered_name = temp_name + else + computer.visible_message("[computer] buzzes rudely.") + else if(href_list["account"]) + var/account_num = text2num(input("Enter account number.", "Account", id_card.associated_account_number)) + id_card.associated_account_number = account_num + if("assign") + if(computer && can_run(user, 1) && id_card) + var/t1 = href_list["assign_target"] + if(t1 == "Custom") + var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45) + //let custom jobs function as an impromptu alt title, mainly for sechuds + if(temp_t) + id_card.assignment = temp_t + else + var/list/access = list() + if(module.is_centcom) + access = get_centcom_access(t1) + else + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(ckey(J.title) == ckey(t1)) + jobdatum = J + break + if(!jobdatum) + to_chat(usr, "No log exists for this job: [t1]") + return + + access = jobdatum.get_access() + + id_card.access = access + id_card.assignment = t1 + id_card.rank = t1 + + callHook("reassign_employee", list(id_card)) + if("access") + if(href_list["allowed"] && computer && can_run(user, 1)) + var/access_type = text2num(href_list["access_target"]) + var/access_allowed = text2num(href_list["allowed"]) + if(access_type in get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM)) + id_card.access -= access_type + if(!access_allowed) + id_card.access += access_type + if(id_card) + id_card.name = text("[id_card.registered_name]'s ID Card ([id_card.assignment])") + + SSnanoui.update_uis(NM) + return 1 diff --git a/code/modules/modular_computers/file_system/programs/command/comm.dm b/code/modules/modular_computers/file_system/programs/command/comm.dm new file mode 100644 index 0000000000..c174ced23b --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/command/comm.dm @@ -0,0 +1,329 @@ +#define STATE_DEFAULT 1 +#define STATE_MESSAGELIST 2 +#define STATE_VIEWMESSAGE 3 +#define STATE_STATUSDISPLAY 4 +#define STATE_ALERT_LEVEL 5 +/datum/computer_file/program/comm + filename = "comm" + filedesc = "Command and Communications Program" + program_icon_state = "comm" + program_key_state = "med_key" + program_menu_icon = "flag" + nanomodule_path = /datum/nano_module/program/comm + extended_desc = "Used to command and control. Can relay long-range communications. This program can not be run on tablet computers." + required_access = access_heads + requires_ntnet = 1 + size = 12 + usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP + network_destination = "long-range communication array" + var/datum/comm_message_listener/message_core = new + +/datum/computer_file/program/comm/clone() + var/datum/computer_file/program/comm/temp = ..() + temp.message_core.messages = null + temp.message_core.messages = message_core.messages.Copy() + return temp + +/datum/nano_module/program/comm + name = "Command and Communications Program" + //available_to_ai = TRUE + var/current_status = STATE_DEFAULT + var/msg_line1 = "" + var/msg_line2 = "" + var/centcomm_message_cooldown = 0 + var/announcment_cooldown = 0 + var/datum/announcement/priority/crew_announcement = new + var/current_viewing_message_id = 0 + var/current_viewing_message = null + +/datum/nano_module/program/comm/New() + ..() + crew_announcement.newscast = 1 + +/datum/nano_module/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + if(program) + data["emagged"] = program.computer_emagged + data["net_comms"] = !!program.get_signal(NTNET_COMMUNICATION) //Double !! is needed to get 1 or 0 answer + data["net_syscont"] = !!program.get_signal(NTNET_SYSTEMCONTROL) + if(program.computer) + data["have_printer"] = !!program.computer.nano_printer + else + data["have_printer"] = 0 + else + data["emagged"] = 0 + data["net_comms"] = 1 + data["net_syscont"] = 1 + data["have_printer"] = 0 + + data["message_line1"] = msg_line1 + data["message_line2"] = msg_line2 + data["state"] = current_status + data["isAI"] = issilicon(usr) + data["authenticated"] = get_authentication_level(user) + data["current_security_level"] = security_level + data["current_security_level_title"] = num2seclevel(security_level) + + data["def_SEC_LEVEL_DELTA"] = SEC_LEVEL_DELTA + data["def_SEC_LEVEL_YELLOW"] = SEC_LEVEL_YELLOW + data["def_SEC_LEVEL_ORANGE"] = SEC_LEVEL_ORANGE + data["def_SEC_LEVEL_VIOLET"] = SEC_LEVEL_VIOLET + data["def_SEC_LEVEL_BLUE"] = SEC_LEVEL_BLUE + data["def_SEC_LEVEL_GREEN"] = SEC_LEVEL_GREEN + + var/datum/comm_message_listener/l = obtain_message_listener() + data["messages"] = l.messages + data["message_deletion_allowed"] = l != global_message_listener + data["message_current_id"] = current_viewing_message_id + if(current_viewing_message) + data["message_current"] = current_viewing_message + + if(emergency_shuttle.location()) + data["have_shuttle"] = 1 + if(emergency_shuttle.online()) + data["have_shuttle_called"] = 1 + else + data["have_shuttle_called"] = 0 + else + data["have_shuttle"] = 0 + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "mod_communication.tmpl", name, 550, 420, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + +/datum/nano_module/program/comm/proc/get_authentication_level(var/mob/user) + if(program) + if(program.can_run(user, 0, access_captain)) + return 2 + else + return program.can_run(user) + return 1 + +/datum/nano_module/program/comm/proc/obtain_message_listener() + if(program) + var/datum/computer_file/program/comm/P = program + return P.message_core + return global_message_listener + +/datum/nano_module/program/comm/Topic(href, href_list) + if(..()) + return 1 + var/mob/user = usr + var/ntn_comm = program ? !!program.get_signal(NTNET_COMMUNICATION) : 1 + var/ntn_cont = program ? !!program.get_signal(NTNET_SYSTEMCONTROL) : 1 + var/datum/comm_message_listener/l = obtain_message_listener() + switch(href_list["action"]) + if("sw_menu") + . = 1 + current_status = text2num(href_list["target"]) + if("announce") + . = 1 + if(get_authentication_level(user) == 2 && !issilicon(usr) && ntn_comm) + if(user) + var/obj/item/weapon/card/id/id_card = user.GetIdCard() + crew_announcement.announcer = GetNameAndAssignmentFromId(id_card) + else + crew_announcement.announcer = "Unknown" + if(announcment_cooldown) + to_chat(usr, "Please allow at least one minute to pass between announcements") + return TRUE + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|text + if(!input || !can_still_topic()) + return 1 + crew_announcement.Announce(input) + announcment_cooldown = 1 + spawn(600)//One minute cooldown + announcment_cooldown = 0 + if("message") + . = 1 + if(href_list["target"] == "emagged") + if(program) + if(get_authentication_level(user) == 2 && program.computer_emagged && !issilicon(usr) && ntn_comm) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) + if(!input || !can_still_topic()) + return 1 + Syndicate_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_say("[key_name(usr)] has made an illegal announcement: [input]") + centcomm_message_cooldown = 1 + spawn(300)//30 second cooldown + centcomm_message_cooldown = 0 + else if(href_list["target"] == "regular") + if(get_authentication_level(user) == 2 && !issilicon(usr) && ntn_comm) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + if(!is_relay_online())//Contact Centcom has a check, Syndie doesn't to allow for Traitor funs. + to_chat(usr, "No Emergency Bluespace Relay detected. Unable to transmit message.") + return 1 + var/input = sanitize(input("Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) + if(!input || !can_still_topic()) + return 1 + CentCom_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_say("[key_name(usr)] has made an IA Centcomm announcement: [input]") + centcomm_message_cooldown = 1 + spawn(300) //30 second cooldown + centcomm_message_cooldown = 0 + if("shuttle") + . = 1 + if(get_authentication_level(user) && ntn_cont) + if(href_list["target"] == "call") + var/confirm = alert("Are you sure you want to call the shuttle?", name, "No", "Yes") + if(confirm == "Yes" && can_still_topic()) + call_shuttle_proc(usr) + + if(href_list["target"] == "cancel" && !issilicon(usr)) + var/confirm = alert("Are you sure you want to cancel the shuttle?", name, "No", "Yes") + if(confirm == "Yes" && can_still_topic()) + cancel_call_proc(usr) + if("setstatus") + . = 1 + if(get_authentication_level(user) && ntn_cont) + switch(href_list["target"]) + if("line1") + var/linput = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", msg_line1) as text|null, 40), 40) + if(can_still_topic()) + msg_line1 = linput + if("line2") + var/linput = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", msg_line2) as text|null, 40), 40) + if(can_still_topic()) + msg_line2 = linput + if("message") + post_status("message", msg_line1, msg_line2) + if("alert") + post_status("alert", href_list["alert"]) + else + post_status(href_list["target"]) + if("setalert") + . = 1 + if(get_authentication_level(user) && !issilicon(usr) && ntn_cont && ntn_comm) + var/current_level = text2num(href_list["target"]) + var/confirm = alert("Are you sure you want to change alert level to [num2seclevel(current_level)]?", name, "No", "Yes") + if(confirm == "Yes" && can_still_topic()) + var/old_level = security_level + if(!current_level) current_level = SEC_LEVEL_GREEN + if(current_level < SEC_LEVEL_GREEN) current_level = SEC_LEVEL_GREEN + if(current_level > SEC_LEVEL_BLUE) current_level = SEC_LEVEL_BLUE //Cannot engage delta with this + set_security_level(current_level) + if(security_level != old_level) + log_game("[key_name(usr)] has changed the security level to [get_security_level()].") + message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") + switch(security_level) + if(SEC_LEVEL_GREEN) + feedback_inc("alert_comms_green",1) + if(SEC_LEVEL_YELLOW) + feedback_inc("alert_comms_yellow",1) + if(SEC_LEVEL_ORANGE) + feedback_inc("alert_comms_orange",1) + if(SEC_LEVEL_VIOLET) + feedback_inc("alert_comms_violet",1) + if(SEC_LEVEL_BLUE) + feedback_inc("alert_comms_blue",1) + else + to_chat(usr, "You press button, but red light flashes and nothing happens.")//This should never happen + + current_status = STATE_DEFAULT + if("viewmessage") + . = 1 + if(get_authentication_level(user) && ntn_comm) + current_viewing_message_id = text2num(href_list["target"]) + for(var/list/m in l.messages) + if(m["id"] == current_viewing_message_id) + current_viewing_message = m + current_status = STATE_VIEWMESSAGE + if("delmessage") + . = 1 + if(get_authentication_level(user) && ntn_comm && l != global_message_listener) + l.Remove(current_viewing_message) + current_status = STATE_MESSAGELIST + if("printmessage") + . = 1 + if(get_authentication_level(user) && ntn_comm) + if(program && program.computer && program.computer.nano_printer) + if(!program.computer.nano_printer.print_text(current_viewing_message["contents"],current_viewing_message["title"])) + to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + else + program.computer.visible_message("\The [program.computer] prints out paper.") + + +/datum/nano_module/program/comm/proc/post_status(var/command, var/data1, var/data2) + + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) + + if(!frequency) return + + + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = 1 + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + log_admin("STATUS: [key_name(usr)] set status screen message with [src]: [data1] [data2]") + if("alert") + status_signal.data["picture_state"] = data1 + + frequency.post_signal(src, status_signal) + +#undef STATE_DEFAULT +#undef STATE_MESSAGELIST +#undef STATE_VIEWMESSAGE +#undef STATE_STATUSDISPLAY +#undef STATE_ALERT_LEVEL + +/* +General message handling stuff +*/ +var/list/comm_message_listeners = list() //We first have to initialize list then we can use it. +var/datum/comm_message_listener/global_message_listener = new //May be used by admins +var/last_message_id = 0 + +proc/get_comm_message_id() + last_message_id = last_message_id + 1 + return last_message_id + +proc/post_comm_message(var/message_title, var/message_text) + var/list/message = list() + message["id"] = get_comm_message_id() + message["title"] = message_title + message["contents"] = message_text + + for (var/datum/comm_message_listener/l in comm_message_listeners) + l.Add(message) + + //Old console support + for (var/obj/machinery/computer/communications/comm in machines) + if (!(comm.stat & (BROKEN | NOPOWER)) && comm.prints_intercept) + var/obj/item/weapon/paper/intercept = new /obj/item/weapon/paper( comm.loc ) + intercept.name = message_title + intercept.info = message_text + + comm.messagetitle.Add(message_title) + comm.messagetext.Add(message_text) + +/datum/comm_message_listener + var/list/messages + +/datum/comm_message_listener/New() + ..() + messages = list() + comm_message_listeners.Add(src) + +/datum/comm_message_listener/proc/Add(var/list/message) + messages[++messages.len] = message + +/datum/comm_message_listener/proc/Remove(var/list/message) + messages -= list(message) diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm similarity index 66% rename from code/modules/nano/modules/alarm_monitor.dm rename to code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm index ec43035ed8..20c4dde1ca 100644 --- a/code/modules/nano/modules/alarm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm @@ -1,88 +1,135 @@ -/datum/nano_module/alarm_monitor - name = "Alarm monitor" - var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use. - var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user. - -/datum/nano_module/alarm_monitor/all/New() - ..() - alarm_handlers = alarm_manager.all_handlers - -/datum/nano_module/alarm_monitor/engineering/New() - ..() - alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm) - -/datum/nano_module/alarm_monitor/security/New() - ..() - alarm_handlers = list(camera_alarm, motion_alarm) - -/datum/nano_module/alarm_monitor/proc/register_alarm(var/object, var/procName) - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.register_alarm(object, procName) - -/datum/nano_module/alarm_monitor/proc/unregister_alarm(var/object) - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister_alarm(object) - -/datum/nano_module/alarm_monitor/proc/all_alarms() - var/list/all_alarms = new() - for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.visible_alarms() - - return all_alarms - -/datum/nano_module/alarm_monitor/proc/major_alarms() - var/list/all_alarms = new() - for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.major_alarms() - - return all_alarms - -/datum/nano_module/alarm_monitor/proc/minor_alarms() - var/list/all_alarms = new() - for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.minor_alarms() - - return all_alarms - -/datum/nano_module/alarm_monitor/Topic(ref, href_list) - if(..()) - return 1 - if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras - if(!C) - return - - usr.switch_to_camera(C) - return 1 - -/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/data[0] - - var/categories[0] - for(var/datum/alarm_handler/AH in alarm_handlers) - categories[++categories.len] = list("category" = AH.category, "alarms" = list()) - for(var/datum/alarm/A in AH.major_alarms()) - var/cameras[0] - var/lost_sources[0] - - if(isAI(user)) - for(var/obj/machinery/camera/C in A.cameras()) - cameras[++cameras.len] = C.nano_structure() - for(var/datum/alarm_source/AS in A.sources) - if(!AS.source) - lost_sources[++lost_sources.len] = AS.source_name - - categories[categories.len]["alarms"] += list(list( - "name" = sanitize(A.alarm_name()), - "origin_lost" = A.origin == null, - "has_cameras" = cameras.len, - "cameras" = cameras, - "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) - data["categories"] = categories - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) +/datum/computer_file/program/alarm_monitor + filename = "alarmmonitoreng" + filedesc = "Alarm Monitoring (Engineering)" + nanomodule_path = /datum/nano_module/alarm_monitor/engineering + ui_header = "alarm_green.gif" + program_icon_state = "alert-green" + program_key_state = "atmos_key" + program_menu_icon = "alert" + extended_desc = "This program provides visual interface for the engineering alarm system." + required_access = access_engine + requires_ntnet = 1 + network_destination = "alarm monitoring network" + size = 5 + var/has_alert = 0 + +/datum/computer_file/program/alarm_monitor/process_tick() + ..() + var/datum/nano_module/alarm_monitor/NMA = NM + if(istype(NMA) && NMA.has_major_alarms()) + if(!has_alert) + program_icon_state = "alert-red" + ui_header = "alarm_red.gif" + update_computer_icon() + has_alert = 1 + else + if(has_alert) + program_icon_state = "alert-green" + ui_header = "alarm_green.gif" + update_computer_icon() + has_alert = 0 + return 1 + +/datum/nano_module/alarm_monitor + name = "Alarm monitor" + var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use. + var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user. + //available_to_ai = FALSE + +/datum/nano_module/alarm_monitor/New() + ..() + alarm_handlers = list() + +/datum/nano_module/alarm_monitor/all/New() + ..() + alarm_handlers = alarm_manager.all_handlers + +/datum/nano_module/alarm_monitor/engineering/New() + ..() + alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm) + +/datum/nano_module/alarm_monitor/security/New() + ..() + alarm_handlers = list(camera_alarm, motion_alarm) + +/datum/nano_module/alarm_monitor/proc/register_alarm(var/object, var/procName) + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.register_alarm(object, procName) + +/datum/nano_module/alarm_monitor/proc/unregister_alarm(var/object) + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister_alarm(object) + +/datum/nano_module/alarm_monitor/proc/all_alarms() + var/list/all_alarms = new() + for(var/datum/alarm_handler/AH in alarm_handlers) + all_alarms += AH.visible_alarms() + + return all_alarms + +/datum/nano_module/alarm_monitor/proc/major_alarms() + var/list/all_alarms = new() + for(var/datum/alarm_handler/AH in alarm_handlers) + all_alarms += AH.major_alarms() + + return all_alarms + +// Modified version of above proc that uses slightly less resources, returns 1 if there is a major alarm, 0 otherwise. +/datum/nano_module/alarm_monitor/proc/has_major_alarms() + for(var/datum/alarm_handler/AH in alarm_handlers) + if(AH.has_major_alarms()) + return 1 + + return 0 + +/datum/nano_module/alarm_monitor/proc/minor_alarms() + var/list/all_alarms = new() + for(var/datum/alarm_handler/AH in alarm_handlers) + all_alarms += AH.minor_alarms() + + return all_alarms + +/datum/nano_module/alarm_monitor/Topic(ref, href_list) + if(..()) + return 1 + if(href_list["switchTo"]) + var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras + if(!C) + return + + usr.switch_to_camera(C) + return 1 + +/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + var/categories[0] + for(var/datum/alarm_handler/AH in alarm_handlers) + categories[++categories.len] = list("category" = AH.category, "alarms" = list()) + for(var/datum/alarm/A in AH.major_alarms()) + var/cameras[0] + var/lost_sources[0] + + if(isAI(user)) + for(var/obj/machinery/camera/C in A.cameras()) + cameras[++cameras.len] = C.nano_structure() + for(var/datum/alarm_source/AS in A.sources) + if(!AS.source) + lost_sources[++lost_sources.len] = AS.source_name + + categories[categories.len]["alarms"] += list(list( + "name" = sanitize(A.alarm_name()), + "origin_lost" = A.origin == null, + "has_cameras" = cameras.len, + "cameras" = cameras, + "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) + data["categories"] = categories + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state) + if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI. + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) \ No newline at end of file diff --git a/code/modules/nano/modules/atmos_control.dm b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm similarity index 78% rename from code/modules/nano/modules/atmos_control.dm rename to code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm index 3924f65af7..776e73ec7a 100644 --- a/code/modules/nano/modules/atmos_control.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm @@ -1,86 +1,103 @@ -/datum/nano_module/atmos_control - name = "Atmospherics Control" - var/obj/access = new() - var/emagged = 0 - var/ui_ref - var/list/monitored_alarms = list() - -/datum/nano_module/atmos_control/New(atmos_computer, req_access, req_one_access, monitored_alarm_ids) - ..() - access.req_access = req_access - access.req_one_access = req_one_access - - if(monitored_alarm_ids) - for(var/obj/machinery/alarm/alarm in machines) - if(alarm.alarm_id && alarm.alarm_id in monitored_alarm_ids) - monitored_alarms += alarm - // machines may not yet be ordered at this point - monitored_alarms = dd_sortedObjectList(monitored_alarms) - -/datum/nano_module/atmos_control/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["alarm"]) - if(ui_ref) - var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms.len ? monitored_alarms : machines) - if(alarm) - var/datum/topic_state/TS = generate_state(alarm) - alarm.ui_interact(usr, master_ui = ui_ref, state = TS) - return 1 - -/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - var/data[0] - var/alarms[0] - var/turf/T = get_turf(nano_host()) - - // TODO: Move these to a cache, similar to cameras - for(var/obj/machinery/alarm/alarm in (monitored_alarms.len ? monitored_alarms : machines)) - if(!monitored_alarms.len && alarm.alarms_hidden) - continue - alarms[++alarms.len] = list( - "name" = sanitize(alarm.name), - "ref"= "\ref[alarm]", - "danger" = max(alarm.danger_level, alarm.alarm_area.atmosalm), - "x" = alarm.x, - "y" = alarm.y, - "z" = alarm.z) - data["alarms"] = alarms - data["map_levels"] = using_map.get_map_levels(T.z) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 625, 625, state = state) - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "atmos_control_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "atmos_control_map_header.tmpl") - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - ui_ref = ui - -/datum/nano_module/atmos_control/proc/generate_state(air_alarm) - var/datum/topic_state/air_alarm/state = new() - state.atmos_control = src - state.air_alarm = air_alarm - return state - -/datum/topic_state/air_alarm - var/datum/nano_module/atmos_control/atmos_control = null - var/obj/machinery/alarm/air_alarm = null - -/datum/topic_state/air_alarm/can_use_topic(var/src_object, var/mob/user) - if(has_access(user)) - return STATUS_INTERACTIVE - return STATUS_UPDATE - -/datum/topic_state/air_alarm/href_list(var/mob/user) - var/list/extra_href = list() - extra_href["remote_connection"] = 1 - extra_href["remote_access"] = has_access(user) - - return extra_href - -/datum/topic_state/air_alarm/proc/has_access(var/mob/user) - return user && (isAI(user) || atmos_control.access.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO)) +/datum/computer_file/program/atmos_control + filename = "atmoscontrol" + filedesc = "Atmosphere Control" + nanomodule_path = /datum/nano_module/atmos_control + program_icon_state = "atmos_control" + program_key_state = "atmos_key" + program_menu_icon = "shuffle" + extended_desc = "This program allows remote control of air alarms. This program can not be run on tablet computers." + required_access = access_atmospherics + requires_ntnet = 1 + network_destination = "atmospheric control system" + requires_ntnet_feature = NTNET_SYSTEMCONTROL + usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE + size = 17 + +/datum/nano_module/atmos_control + name = "Atmospherics Control" + var/obj/access = new() + var/emagged = 0 + var/ui_ref + var/list/monitored_alarms = list() + +/datum/nano_module/atmos_control/New(atmos_computer, req_access, req_one_access, monitored_alarm_ids) + ..() + access.req_access = req_access + access.req_one_access = req_one_access + + if(monitored_alarm_ids) + for(var/obj/machinery/alarm/alarm in machines) + if(alarm.alarm_id && alarm.alarm_id in monitored_alarm_ids) + monitored_alarms += alarm + // machines may not yet be ordered at this point + monitored_alarms = dd_sortedObjectList(monitored_alarms) + +/datum/nano_module/atmos_control/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["alarm"]) + if(ui_ref) + var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms.len ? monitored_alarms : machines) + if(alarm) + var/datum/topic_state/TS = generate_state(alarm) + alarm.ui_interact(usr, master_ui = ui_ref, state = TS) + return 1 + +/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + var/alarms[0] + var/turf/T = get_turf(nano_host()) + + // TODO: Move these to a cache, similar to cameras + for(var/obj/machinery/alarm/alarm in (monitored_alarms.len ? monitored_alarms : machines)) + if(!monitored_alarms.len && alarm.alarms_hidden) + continue + alarms[++alarms.len] = list( + "name" = sanitize(alarm.name), + "ref"= "\ref[alarm]", + "danger" = max(alarm.danger_level, alarm.alarm_area.atmosalm), + "x" = alarm.x, + "y" = alarm.y, + "z" = alarm.z) + data["alarms"] = alarms + data["map_levels"] = using_map.get_map_levels(T.z) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 625, 625, state = state) + if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI. + ui.auto_update_layout = 1 + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "atmos_control_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "atmos_control_map_header.tmpl") + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(0) + ui_ref = ui + +/datum/nano_module/atmos_control/proc/generate_state(air_alarm) + var/datum/topic_state/air_alarm/state = new() + state.atmos_control = src + state.air_alarm = air_alarm + return state + +/datum/topic_state/air_alarm + var/datum/nano_module/atmos_control/atmos_control = null + var/obj/machinery/alarm/air_alarm = null + +/datum/topic_state/air_alarm/can_use_topic(var/src_object, var/mob/user) + if(has_access(user)) + return STATUS_INTERACTIVE + return STATUS_UPDATE + +/datum/topic_state/air_alarm/href_list(var/mob/user) + var/list/extra_href = list() + extra_href["remote_connection"] = 1 + extra_href["remote_access"] = has_access(user) + + return extra_href + +/datum/topic_state/air_alarm/proc/has_access(var/mob/user) + return user && (isAI(user) || atmos_control.access.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO) || (access_ce in user.GetAccess())) diff --git a/code/modules/nano/modules/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm similarity index 60% rename from code/modules/nano/modules/power_monitor.dm rename to code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index ae5dc44f29..32117e845c 100644 --- a/code/modules/nano/modules/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -1,65 +1,113 @@ -/datum/nano_module/power_monitor - name = "Power monitor" - var/list/grid_sensors - var/active_sensor = null //name_tag of the currently selected sensor - -/datum/nano_module/power_monitor/New() - ..() - refresh_sensors() - -/datum/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = list() - var/list/sensors = list() - // Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading. - var/obj/machinery/power/sensor/focus = null - var/turf/T = get_turf(nano_host()) - - // Build list of data from sensor readings. - for(var/obj/machinery/power/sensor/S in grid_sensors) - sensors.Add(list(list( - "name" = S.name_tag, - "alarm" = S.check_grid_warning() - ))) - if(S.name_tag == active_sensor) - focus = S - - data["all_sensors"] = sensors - if(focus) - data["focus"] = focus.return_reading_data() - data["map_levels"] = using_map.get_map_levels(T.z) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "power_monitor.tmpl", "Power Monitoring Console", 800, 500, state = state) - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "power_monitor_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "power_monitor_map_header.tmpl") - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -// Refreshes list of active sensors kept on this computer. -/datum/nano_module/power_monitor/proc/refresh_sensors() - grid_sensors = list() - var/turf/T = get_turf(nano_host()) - var/list/levels = list() - if(T) - levels += using_map.get_map_levels(T.z, FALSE) - for(var/obj/machinery/power/sensor/S in machines) - if(T && (S.loc.z == T.z) || (S.loc.z in levels) || (S.long_range)) // Consoles have range on their Z-Level. Sensors with long_range var will work between Z levels. - if(S.name_tag == "#UNKN#") // Default name. Shouldn't happen! - warning("Powernet sensor with unset ID Tag! [S.x]X [S.y]Y [S.z]Z") - else - grid_sensors += S - -// Allows us to process UI clicks, which are relayed in form of hrefs. -/datum/nano_module/power_monitor/Topic(href, href_list) - if(..()) - return 1 - if( href_list["clear"] ) - active_sensor = null - if( href_list["refresh"] ) - refresh_sensors() - else if( href_list["setsensor"] ) - active_sensor = href_list["setsensor"] +/datum/computer_file/program/power_monitor + filename = "powermonitor" + filedesc = "Power Monitoring" + nanomodule_path = /datum/nano_module/power_monitor/ + program_icon_state = "power_monitor" + program_key_state = "power_key" + program_menu_icon = "battery-3" + extended_desc = "This program connects to sensors to provide information about electrical systems" + ui_header = "power_norm.gif" + required_access = access_engine + requires_ntnet = 1 + network_destination = "power monitoring system" + size = 9 + var/has_alert = 0 + +/datum/computer_file/program/power_monitor/process_tick() + ..() + var/datum/nano_module/power_monitor/NMA = NM + if(istype(NMA) && NMA.has_alarm()) + if(!has_alert) + program_icon_state = "power_monitor_warn" + ui_header = "power_warn.gif" + update_computer_icon() + has_alert = 1 + else + if(has_alert) + program_icon_state = "power_monitor" + ui_header = "power_norm.gif" + update_computer_icon() + has_alert = 0 + +/datum/nano_module/power_monitor + name = "Power monitor" + var/list/grid_sensors + var/active_sensor = null //name_tag of the currently selected sensor + +/datum/nano_module/power_monitor/New() + ..() + refresh_sensors() + +// Checks whether there is an active alarm, if yes, returns 1, otherwise returns 0. +/datum/nano_module/power_monitor/proc/has_alarm() + for(var/obj/machinery/power/sensor/S in grid_sensors) + if(S.check_grid_warning()) + return 1 + return 0 + +// If PC is not null header template is loaded. Use PC.get_header_data() to get relevant nanoui data from it. All data entries begin with "PC_...." +// In future it may be expanded to other modular computer devices. +/datum/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + var/list/sensors = list() + // Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading. + var/obj/machinery/power/sensor/focus = null + var/turf/T = get_turf(nano_host()) + + // Build list of data from sensor readings. + for(var/obj/machinery/power/sensor/S in grid_sensors) + sensors.Add(list(list( + "name" = S.name_tag, + "alarm" = S.check_grid_warning() + ))) + if(S.name_tag == active_sensor) + focus = S + + data["all_sensors"] = sensors + if(focus) + data["focus"] = focus.return_reading_data() + data["map_levels"] = using_map.get_map_levels(T.z) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "power_monitor.tmpl", "Power Monitoring Console", 800, 500, state = state) + if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI. + ui.auto_update_layout = 1 + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "power_monitor_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "power_monitor_map_header.tmpl") + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// Refreshes list of active sensors kept on this computer. +/datum/nano_module/power_monitor/proc/refresh_sensors() + grid_sensors = list() + var/turf/T = get_turf(nano_host()) + var/list/levels = list() + if(!T) // Safety check + return + if(T) + levels += using_map.get_map_levels(T.z, FALSE) + for(var/obj/machinery/power/sensor/S in machines) + if(T && (S.loc.z == T.z) || (S.loc.z in levels) || (S.long_range)) // Consoles have range on their Z-Level. Sensors with long_range var will work between Z levels. + if(S.name_tag == "#UNKN#") // Default name. Shouldn't happen! + warning("Powernet sensor with unset ID Tag! [S.x]X [S.y]Y [S.z]Z") + else + grid_sensors += S + +// Allows us to process UI clicks, which are relayed in form of hrefs. +/datum/nano_module/power_monitor/Topic(href, href_list) + if(..()) + return 1 + if( href_list["clear"] ) + active_sensor = null + . = 1 + if( href_list["refresh"] ) + refresh_sensors() + . = 1 + else if( href_list["setsensor"] ) + active_sensor = href_list["setsensor"] + . = 1 diff --git a/code/modules/nano/modules/rcon.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm similarity index 82% rename from code/modules/nano/modules/rcon.dm rename to code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index 65c1acfe36..d5ee88b670 100644 --- a/code/modules/nano/modules/rcon.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -1,115 +1,132 @@ -/datum/nano_module/rcon - name = "Power RCON" - var/list/known_SMESs = null - var/list/known_breakers = null - // Allows you to hide specific parts of the UI - var/hide_SMES = 0 - var/hide_SMES_details = 0 - var/hide_breakers = 0 - -/datum/nano_module/rcon/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1, var/datum/topic_state/state = default_state) - FindDevices() // Update our devices list - var/data[0] - - // SMES DATA (simplified view) - var/list/smeslist[0] - for(var/obj/machinery/power/smes/buildable/SMES in known_SMESs) - smeslist.Add(list(list( - "charge" = round(SMES.Percentage()), - "input_set" = SMES.input_attempt, - "input_val" = round(SMES.input_level/1000, 0.1), - "output_set" = SMES.output_attempt, - "output_val" = round(SMES.output_level/1000, 0.1), - "output_load" = round(SMES.output_used/1000, 0.1), - "RCON_tag" = SMES.RCon_tag - ))) - - data["smes_info"] = sortByKey(smeslist, "RCON_tag") - - // BREAKER DATA (simplified view) - var/list/breakerlist[0] - for(var/obj/machinery/power/breakerbox/BR in known_breakers) - breakerlist.Add(list(list( - "RCON_tag" = BR.RCon_tag, - "enabled" = BR.on - ))) - data["breaker_info"] = breakerlist - data["hide_smes"] = hide_SMES - data["hide_smes_details"] = hide_SMES_details - data["hide_breakers"] = hide_breakers - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "rcon.tmpl", "RCON Console", 600, 400, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -// Proc: Topic() -// Parameters: 2 (href, href_list - allows us to process UI clicks) -// Description: Allows us to process UI clicks, which are relayed in form of hrefs. -/datum/nano_module/rcon/Topic(href, href_list) - if(..()) - return - - if(href_list["smes_in_toggle"]) - var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_toggle"]) - if(SMES) - SMES.toggle_input() - if(href_list["smes_out_toggle"]) - var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_toggle"]) - if(SMES) - SMES.toggle_output() - if(href_list["smes_in_set"]) - var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_set"]) - if(SMES) - var/inputset = (input(usr, "Enter new input level (0-[SMES.input_level_max/1000] kW)", "SMES Input Power Control", SMES.input_level/1000) as num) * 1000 - SMES.set_input(inputset) - if(href_list["smes_out_set"]) - var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_set"]) - if(SMES) - var/outputset = (input(usr, "Enter new output level (0-[SMES.output_level_max/1000] kW)", "SMES Output Power Control", SMES.output_level/1000) as num) * 1000 - SMES.set_output(outputset) - - if(href_list["toggle_breaker"]) - var/obj/machinery/power/breakerbox/toggle = null - for(var/obj/machinery/power/breakerbox/breaker in known_breakers) - if(breaker.RCon_tag == href_list["toggle_breaker"]) - toggle = breaker - if(toggle) - if(toggle.update_locked) - usr << "The breaker box was recently toggled. Please wait before toggling it again." - else - toggle.auto_toggle() - if(href_list["hide_smes"]) - hide_SMES = !hide_SMES - if(href_list["hide_smes_details"]) - hide_SMES_details = !hide_SMES_details - if(href_list["hide_breakers"]) - hide_breakers = !hide_breakers - - -// Proc: GetSMESByTag() -// Parameters: 1 (tag - RCON tag of SMES we want to look up) -// Description: Looks up and returns SMES which has matching RCON tag -/datum/nano_module/rcon/proc/GetSMESByTag(var/tag) - if(!tag) - return - - for(var/obj/machinery/power/smes/buildable/S in known_SMESs) - if(S.RCon_tag == tag) - return S - -// Proc: FindDevices() -// Parameters: None -// Description: Refreshes local list of known devices. -/datum/nano_module/rcon/proc/FindDevices() - known_SMESs = new /list() - for(var/obj/machinery/power/smes/buildable/SMES in machines) - if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon) - known_SMESs.Add(SMES) - - known_breakers = new /list() - for(var/obj/machinery/power/breakerbox/breaker in machines) - if(breaker.RCon_tag != "NO_TAG") - known_breakers.Add(breaker) +/datum/computer_file/program/rcon_console + filename = "rconconsole" + filedesc = "RCON Remote Control" + nanomodule_path = /datum/nano_module/rcon + program_icon_state = "generic" + program_key_state = "rd_key" + program_menu_icon = "power" + extended_desc = "This program allows remote control of power distribution systems. This program can not be run on tablet computers." + required_access = access_engine + requires_ntnet = 1 + network_destination = "RCON remote control system" + requires_ntnet_feature = NTNET_SYSTEMCONTROL + usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE + size = 19 + +/datum/nano_module/rcon + name = "Power RCON" + var/list/known_SMESs = null + var/list/known_breakers = null + // Allows you to hide specific parts of the UI + var/hide_SMES = 0 + var/hide_SMES_details = 0 + var/hide_breakers = 0 + +/datum/nano_module/rcon/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1, var/datum/topic_state/state = default_state) + FindDevices() // Update our devices list + var/list/data = host.initial_data() + + // SMES DATA (simplified view) + var/list/smeslist[0] + for(var/obj/machinery/power/smes/buildable/SMES in known_SMESs) + smeslist.Add(list(list( + "charge" = round(SMES.Percentage()), + "input_set" = SMES.input_attempt, + "input_val" = round(SMES.input_level/1000, 0.1), + "output_set" = SMES.output_attempt, + "output_val" = round(SMES.output_level/1000, 0.1), + "output_load" = round(SMES.output_used/1000, 0.1), + "RCON_tag" = SMES.RCon_tag + ))) + + data["smes_info"] = sortByKey(smeslist, "RCON_tag") + + // BREAKER DATA (simplified view) + var/list/breakerlist[0] + for(var/obj/machinery/power/breakerbox/BR in known_breakers) + breakerlist.Add(list(list( + "RCON_tag" = BR.RCon_tag, + "enabled" = BR.on + ))) + data["breaker_info"] = breakerlist + data["hide_smes"] = hide_SMES + data["hide_smes_details"] = hide_SMES_details + data["hide_breakers"] = hide_breakers + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "rcon.tmpl", "RCON Console", 600, 400, state = state) + if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI. + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// Proc: Topic() +// Parameters: 2 (href, href_list - allows us to process UI clicks) +// Description: Allows us to process UI clicks, which are relayed in form of hrefs. +/datum/nano_module/rcon/Topic(href, href_list) + if(..()) + return + + if(href_list["smes_in_toggle"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_toggle"]) + if(SMES) + SMES.toggle_input() + if(href_list["smes_out_toggle"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_toggle"]) + if(SMES) + SMES.toggle_output() + if(href_list["smes_in_set"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_set"]) + if(SMES) + var/inputset = (input(usr, "Enter new input level (0-[SMES.input_level_max/1000] kW)", "SMES Input Power Control", SMES.input_level/1000) as num) * 1000 + SMES.set_input(inputset) + if(href_list["smes_out_set"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_set"]) + if(SMES) + var/outputset = (input(usr, "Enter new output level (0-[SMES.output_level_max/1000] kW)", "SMES Output Power Control", SMES.output_level/1000) as num) * 1000 + SMES.set_output(outputset) + + if(href_list["toggle_breaker"]) + var/obj/machinery/power/breakerbox/toggle = null + for(var/obj/machinery/power/breakerbox/breaker in known_breakers) + if(breaker.RCon_tag == href_list["toggle_breaker"]) + toggle = breaker + if(toggle) + if(toggle.update_locked) + to_chat(usr, "The breaker box was recently toggled. Please wait before toggling it again.") + else + toggle.auto_toggle() + if(href_list["hide_smes"]) + hide_SMES = !hide_SMES + if(href_list["hide_smes_details"]) + hide_SMES_details = !hide_SMES_details + if(href_list["hide_breakers"]) + hide_breakers = !hide_breakers + + +// Proc: GetSMESByTag() +// Parameters: 1 (tag - RCON tag of SMES we want to look up) +// Description: Looks up and returns SMES which has matching RCON tag +/datum/nano_module/rcon/proc/GetSMESByTag(var/tag) + if(!tag) + return + + for(var/obj/machinery/power/smes/buildable/S in known_SMESs) + if(S.RCon_tag == tag) + return S + +// Proc: FindDevices() +// Parameters: None +// Description: Refreshes local list of known devices. +/datum/nano_module/rcon/proc/FindDevices() + known_SMESs = new /list() + for(var/obj/machinery/power/smes/buildable/SMES in machines) + if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon) + known_SMESs.Add(SMES) + + known_breakers = new /list() + for(var/obj/machinery/power/breakerbox/breaker in machines) + if(breaker.RCon_tag != "NO_TAG") + known_breakers.Add(breaker) diff --git a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm new file mode 100644 index 0000000000..af0489a953 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm @@ -0,0 +1,132 @@ +/datum/computer_file/program/supermatter_monitor + filename = "supmon" + filedesc = "Supermatter Monitoring" + nanomodule_path = /datum/nano_module/supermatter_monitor/ + program_icon_state = "smmon_0" + program_key_state = "tech_key" + program_menu_icon = "notice" + extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." + ui_header = "smmon_0.gif" + required_access = access_engine + requires_ntnet = 1 + network_destination = "supermatter monitoring system" + size = 5 + var/last_status = 0 + +/datum/computer_file/program/supermatter_monitor/process_tick() + ..() + var/datum/nano_module/supermatter_monitor/NMS = NM + var/new_status = istype(NMS) ? NMS.get_status() : 0 + if(last_status != new_status) + last_status = new_status + ui_header = "smmon_[last_status].gif" + program_icon_state = "smmon_[last_status]" + if(istype(computer)) + computer.update_icon() + +/datum/nano_module/supermatter_monitor + name = "Supermatter monitor" + var/list/supermatters + var/obj/machinery/power/supermatter/active = null // Currently selected supermatter crystal. + +/datum/nano_module/supermatter_monitor/Destroy() + . = ..() + active = null + supermatters = null + +/datum/nano_module/supermatter_monitor/New() + ..() + refresh() + +// Refreshes list of active supermatter crystals +/datum/nano_module/supermatter_monitor/proc/refresh() + supermatters = list() + var/turf/T = get_turf(nano_host()) + if(!T) + return + var/valid_z_levels = (GetConnectedZlevels(T.z) & using_map.station_levels) + for(var/obj/machinery/power/supermatter/S in machines) + // Delaminating, not within coverage, not on a tile. + if(S.grav_pulling || S.exploded || !(S.z in valid_z_levels) || !istype(S.loc, /turf/)) + continue + supermatters.Add(S) + + if(!(active in supermatters)) + active = null + +/datum/nano_module/supermatter_monitor/proc/get_status() + . = SUPERMATTER_INACTIVE + for(var/obj/machinery/power/supermatter/S in supermatters) + . = max(., S.get_status()) + +/datum/nano_module/supermatter_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + if(istype(active)) + var/turf/T = get_turf(active) + if(!T) + active = null + return + var/datum/gas_mixture/air = T.return_air() + if(!istype(air)) + active = null + return + + data["active"] = 1 + data["SM_integrity"] = active.get_integrity() + data["SM_power"] = active.power + data["SM_ambienttemp"] = air.temperature + data["SM_ambientpressure"] = air.return_pressure() + //data["SM_EPR"] = active.get_epr() + if(air.total_moles) + data["SM_gas_O2"] = round(100*air.gas["oxygen"]/air.total_moles,0.01) + data["SM_gas_CO2"] = round(100*air.gas["carbon_dioxide"]/air.total_moles,0.01) + data["SM_gas_N2"] = round(100*air.gas["nitrogen"]/air.total_moles,0.01) + data["SM_gas_PH"] = round(100*air.gas["phoron"]/air.total_moles,0.01) + data["SM_gas_N2O"] = round(100*air.gas["sleeping_agent"]/air.total_moles,0.01) + else + data["SM_gas_O2"] = 0 + data["SM_gas_CO2"] = 0 + data["SM_gas_N2"] = 0 + data["SM_gas_PH"] = 0 + data["SM_gas_N2O"] = 0 + else + var/list/SMS = list() + for(var/obj/machinery/power/supermatter/S in supermatters) + var/area/A = get_area(S) + if(!A) + continue + + SMS.Add(list(list( + "area_name" = A.name, + "integrity" = S.get_integrity(), + "uid" = S.uid + ))) + + data["active"] = 0 + data["supermatters"] = SMS + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "supermatter_monitor.tmpl", "Supermatter Monitoring", 600, 400, state = state) + if(host.update_layout()) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/nano_module/supermatter_monitor/Topic(href, href_list) + if(..()) + return 1 + if( href_list["clear"] ) + active = null + return 1 + if( href_list["refresh"] ) + refresh() + return 1 + if( href_list["set"] ) + var/newuid = text2num(href_list["set"]) + for(var/obj/machinery/power/supermatter/S in supermatters) + if(S.uid == newuid) + active = S + return 1 \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/camera.dm b/code/modules/modular_computers/file_system/programs/generic/camera.dm new file mode 100644 index 0000000000..a722dea6b6 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/camera.dm @@ -0,0 +1,193 @@ +// Returns which access is relevant to passed network. Used by the program. +/proc/get_camera_access(var/network) + if(!network) + return 0 + . = using_map.get_network_access(network) + if(.) + return + + switch(network) + if(NETWORK_THUNDER) + return 0 + if(NETWORK_ENGINE,NETWORK_ALARM_ATMOS,NETWORK_ALARM_FIRE,NETWORK_ALARM_POWER) + return access_engine + if(NETWORK_CIRCUITS) + return access_research + if(NETWORK_ERT) + return access_cent_specops + + if(network in using_map.station_networks) + return access_security // Default for all other station networks + else + return 999 //Inaccessible if not a station network and not mentioned above + +/datum/computer_file/program/camera_monitor + filename = "cammon" + filedesc = "Camera Monitoring" + nanomodule_path = /datum/nano_module/camera_monitor + program_icon_state = "cameras" + program_key_state = "generic_key" + program_menu_icon = "search" + extended_desc = "This program allows remote access to the camera system. Most camera networks may have additional access requirements." + size = 12 + available_on_ntnet = 1 + requires_ntnet = 1 + +/datum/nano_module/camera_monitor + name = "Camera Monitoring program" + var/obj/machinery/camera/current_camera = null + var/current_network = null + +/datum/nano_module/camera_monitor/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, state = default_state) + var/list/data = host.initial_data() + + data["current_camera"] = current_camera ? current_camera.nano_structure() : null + data["current_network"] = current_network + + var/list/all_networks[0] + for(var/network in using_map.station_networks) + if(can_access_network(user, get_camera_access(network), 1)) + all_networks.Add(list(list( + "tag" = network, + "has_access" = 1 + ))) + for(var/network in using_map.secondary_networks) + if(can_access_network(user, get_camera_access(network), 0)) + all_networks.Add(list(list( + "tag" = network, + "has_access" = 1 + ))) + + all_networks = modify_networks_list(all_networks) + + data["networks"] = all_networks + + if(current_network) + data["cameras"] = camera_repository.cameras_in_network(current_network) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "mod_sec_camera.tmpl", "Camera Monitoring", 900, 800) + // ui.auto_update_layout = 1 // Disabled as with suit sensors monitor - breaks the UI map. Re-enable once it's fixed somehow. + + ui.add_template("mapContent", "sec_camera_map_content.tmpl") + ui.add_template("mapHeader", "mod_sec_camera_map_header.tmpl") + ui.set_initial_data(data) + ui.open() + +// Intended to be overriden by subtypes to manually add non-station networks to the list. +/datum/nano_module/camera_monitor/proc/modify_networks_list(var/list/networks) + return networks + +/datum/nano_module/camera_monitor/proc/can_access_network(var/mob/user, var/network_access, var/station_network = 0) + // No access passed, or 0 which is considered no access requirement. Allow it. + if(!network_access) + return 1 + + if(station_network) + return check_access(user, network_access) || check_access(user, access_security) || check_access(user, access_heads) + else + return check_access(user, network_access) + +/datum/nano_module/camera_monitor/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["switch_camera"]) + var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras + if(!C) + return + if(!(current_network in C.network)) + return + + switch_to_camera(usr, C) + return 1 + + else if(href_list["switch_network"]) + // Either security access, or access to the specific camera network's department is required in order to access the network. + if(can_access_network(usr, get_camera_access(href_list["switch_network"]), (href_list["switch_network"] in using_map.station_networks))) + current_network = href_list["switch_network"] + else + to_chat(usr, "\The [nano_host()] shows an \"Network Access Denied\" error message.") + return 1 + + else if(href_list["reset"]) + reset_current() + usr.reset_view(current_camera) + return 1 + +/datum/nano_module/camera_monitor/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) + //don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras. + if(isAI(user)) + var/mob/living/silicon/ai/A = user + // Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise. + if(!A.is_in_chassis()) + return 0 + + A.eyeobj.setLoc(get_turf(C)) + A.client.eye = A.eyeobj + return 1 + + set_current(C) + user.machine = nano_host() + user.reset_view(C) + return 1 + +/datum/nano_module/camera_monitor/proc/set_current(var/obj/machinery/camera/C) + if(current_camera == C) + return + + if(current_camera) + reset_current() + + current_camera = C + if(current_camera) + var/mob/living/L = current_camera.loc + if(istype(L)) + L.tracking_initiated() + +/datum/nano_module/camera_monitor/proc/reset_current() + if(current_camera) + var/mob/living/L = current_camera.loc + if(istype(L)) + L.tracking_cancelled() + current_camera = null + +/datum/nano_module/camera_monitor/check_eye(var/mob/user as mob) + if(!current_camera) + return 0 + var/viewflag = current_camera.check_eye(user) + if ( viewflag < 0 ) //camera doesn't work + reset_current() + return viewflag + + +// ERT Variant of the program +/datum/computer_file/program/camera_monitor/ert + filename = "ntcammon" + filedesc = "Advanced Camera Monitoring" + extended_desc = "This program allows remote access to the camera system. Some camera networks may have additional access requirements. This version has an integrated database with additional encrypted keys." + size = 14 + nanomodule_path = /datum/nano_module/camera_monitor/ert + available_on_ntnet = 0 + +/datum/nano_module/camera_monitor/ert + name = "Advanced Camera Monitoring Program" + //available_to_ai = FALSE + +// The ERT variant has access to ERT and crescent cams, but still checks for accesses. ERT members should be able to use it. +/datum/nano_module/camera_monitor/ert/modify_networks_list(var/list/networks) + ..() + networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1))) + networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1))) + return networks + +/datum/nano_module/camera_monitor/apply_visual(mob/M) + if(current_camera) + current_camera.apply_visual(M) + else + remove_visual(M) + +/datum/nano_module/camera_monitor/remove_visual(mob/M) + if(current_camera) + current_camera.remove_visual(M) diff --git a/code/modules/modular_computers/file_system/programs/generic/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm new file mode 100644 index 0000000000..c3ce1e358a --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/configurator.dm @@ -0,0 +1,64 @@ +// This is special hardware configuration program. +// It is to be used only with modular computers. +// It allows you to toggle components of your device. + +/datum/computer_file/program/computerconfig + filename = "compconfig" + filedesc = "Computer Configuration Tool" + extended_desc = "This program allows configuration of computer's hardware" + program_icon_state = "generic" + program_key_state = "generic_key" + program_menu_icon = "gear" + unsendable = 1 + undeletable = 1 + size = 4 + available_on_ntnet = 0 + requires_ntnet = 0 + nanomodule_path = /datum/nano_module/program/computer_configurator/ + +/datum/nano_module/program/computer_configurator + name = "NTOS Computer Configuration Tool" + var/obj/item/modular_computer/movable = null + +/datum/nano_module/program/computer_configurator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(program) + movable = program.computer + if(!istype(movable)) + movable = null + + // No computer connection, we can't get data from that. + if(!movable) + return 0 + + var/list/data = list() + + if(program) + data = program.get_header_data() + + var/list/hardware = movable.get_all_components() + + data["disk_size"] = movable.hard_drive.max_capacity + data["disk_used"] = movable.hard_drive.used_capacity + data["power_usage"] = movable.last_power_usage + data["battery_exists"] = movable.battery_module ? 1 : 0 + if(movable.battery_module) + data["battery_rating"] = movable.battery_module.battery.maxcharge + data["battery_percent"] = round(movable.battery_module.battery.percent()) + + var/list/all_entries[0] + for(var/obj/item/weapon/computer_hardware/H in hardware) + all_entries.Add(list(list( + "name" = H.name, + "desc" = H.desc, + "enabled" = H.enabled, + "critical" = H.critical, + "powerusage" = H.power_usage + ))) + + data["hardware"] = all_entries + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "laptop_configuration.tmpl", "NTOS Configuration Utility", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/email_client.dm b/code/modules/modular_computers/file_system/programs/generic/email_client.dm new file mode 100644 index 0000000000..3e2f979bb5 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/email_client.dm @@ -0,0 +1,499 @@ +/datum/computer_file/program/email_client + filename = "emailc" + filedesc = "Email Client" + extended_desc = "This program may be used to log in into your email account." + program_icon_state = "generic" + program_key_state = "generic_key" + program_menu_icon = "mail-closed" + size = 7 + requires_ntnet = 1 + available_on_ntnet = 1 + var/stored_login = "" + var/stored_password = "" + + nanomodule_path = /datum/nano_module/email_client + +// Persistency. Unless you log out, or unless your password changes, this will pre-fill the login data when restarting the program +/datum/computer_file/program/email_client/kill_program() + if(NM) + var/datum/nano_module/email_client/NME = NM + if(NME.current_account) + stored_login = NME.stored_login + stored_password = NME.stored_password + else + stored_login = "" + stored_password = "" + . = ..() + +/datum/computer_file/program/email_client/run_program() + . = ..() + if(NM) + var/datum/nano_module/email_client/NME = NM + NME.stored_login = stored_login + NME.stored_password = stored_password + NME.log_in() + NME.error = "" + NME.check_for_new_messages(1) + +/datum/computer_file/program/email_client/proc/new_mail_notify() + computer.visible_message("\The [computer] beeps softly, indicating a new email has been received.", 1) + +/datum/computer_file/program/email_client/process_tick() + ..() + var/datum/nano_module/email_client/NME = NM + if(!istype(NME)) + return + NME.relayed_process(ntnet_speed) + + var/check_count = NME.check_for_new_messages() + if(check_count) + if(check_count == 2) + new_mail_notify() + ui_header = "ntnrc_new.gif" + else + ui_header = "ntnrc_idle.gif" + +/datum/nano_module/email_client/ + name = "Email Client" + var/stored_login = "" + var/stored_password = "" + var/error = "" + + var/msg_title = "" + var/msg_body = "" + var/msg_recipient = "" + var/datum/computer_file/msg_attachment = null + var/folder = "Inbox" + var/addressbook = FALSE + var/new_message = FALSE + + var/last_message_count = 0 // How many messages were there during last check. + var/read_message_count = 0 // How many messages were there when user has last accessed the UI. + + var/datum/computer_file/downloading = null + var/download_progress = 0 + var/download_speed = 0 + + var/datum/computer_file/data/email_account/current_account = null + var/datum/computer_file/data/email_message/current_message = null + +/datum/nano_module/email_client/proc/log_in() + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(!account.can_login) + continue + if(account.login == stored_login) + if(account.password == stored_password) + if(account.suspended) + error = "This account has been suspended. Please contact the system administrator for assistance." + return 0 + current_account = account + return 1 + else + error = "Invalid Password" + return 0 + error = "Invalid Login" + return 0 + +// Returns 0 if no new messages were received, 1 if there is an unread message but notification has already been sent. +// and 2 if there is a new message that appeared in this tick (and therefore notification should be sent by the program). +/datum/nano_module/email_client/proc/check_for_new_messages(var/messages_read = FALSE) + if(!current_account) + return 0 + + var/list/allmails = current_account.all_emails() + + if(allmails.len > last_message_count) + . = 2 + else if(allmails.len > read_message_count) + . = 1 + else + . = 0 + + last_message_count = allmails.len + if(messages_read) + read_message_count = allmails.len + + +/datum/nano_module/email_client/proc/log_out() + current_account = null + downloading = null + download_progress = 0 + last_message_count = 0 + read_message_count = 0 + +/datum/nano_module/email_client/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + // Password has been changed by other client connected to this email account + if(current_account) + if(current_account.password != stored_password) + log_out() + error = "Invalid Password" + // Banned. + if(current_account.suspended) + log_out() + error = "This account has been suspended. Please contact the system administrator for assistance." + + if(error) + data["error"] = error + else if(downloading) + data["downloading"] = 1 + data["down_filename"] = "[downloading.filename].[downloading.filetype]" + data["down_progress"] = download_progress + data["down_size"] = downloading.size + data["down_speed"] = download_speed + + else if(istype(current_account)) + data["current_account"] = current_account.login + if(addressbook) + var/list/all_accounts = list() + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(!account.can_login) + continue + all_accounts.Add(list(list( + "login" = account.login + ))) + data["addressbook"] = 1 + data["accounts"] = all_accounts + else if(new_message) + data["new_message"] = 1 + data["msg_title"] = msg_title + data["msg_body"] = pencode2html(msg_body) + data["msg_recipient"] = msg_recipient + if(msg_attachment) + data["msg_hasattachment"] = 1 + data["msg_attachment_filename"] = "[msg_attachment.filename].[msg_attachment.filetype]" + data["msg_attachment_size"] = msg_attachment.size + else if (current_message) + data["cur_title"] = current_message.title + data["cur_body"] = pencode2html(current_message.stored_data) + data["cur_timestamp"] = current_message.timestamp + data["cur_source"] = current_message.source + data["cur_uid"] = current_message.uid + if(istype(current_message.attachment)) + data["cur_hasattachment"] = 1 + data["cur_attachment_filename"] = "[current_message.attachment.filename].[current_message.attachment.filetype]" + data["cur_attachment_size"] = current_message.attachment.size + else + data["label_inbox"] = "Inbox ([current_account.inbox.len])" + data["label_spam"] = "Spam ([current_account.spam.len])" + data["label_deleted"] = "Deleted ([current_account.deleted.len])" + var/list/message_source + if(folder == "Inbox") + message_source = current_account.inbox + else if(folder == "Spam") + message_source = current_account.spam + else if(folder == "Deleted") + message_source = current_account.deleted + + if(message_source) + data["folder"] = folder + var/list/all_messages = list() + for(var/datum/computer_file/data/email_message/message in message_source) + all_messages.Add(list(list( + "title" = message.title, + "body" = pencode2html(message.stored_data), + "source" = message.source, + "timestamp" = message.timestamp, + "uid" = message.uid + ))) + data["messages"] = all_messages + data["messagecount"] = all_messages.len + else + data["stored_login"] = stored_login + data["stored_password"] = stars(stored_password, 0) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "email_client.tmpl", "Email Client", 600, 450, state = state) + if(host.update_layout()) + ui.auto_update_layout = 1 + ui.set_auto_update(1) + ui.set_initial_data(data) + ui.open() + +/datum/nano_module/email_client/proc/find_message_by_fuid(var/fuid) + if(!istype(current_account)) + return + + // href_list works with strings, so this makes it a bit easier for us + if(istext(fuid)) + fuid = text2num(fuid) + + for(var/datum/computer_file/data/email_message/message in current_account.all_emails()) + if(message.uid == fuid) + return message + +/datum/nano_module/email_client/proc/clear_message() + new_message = FALSE + msg_title = "" + msg_body = "" + msg_recipient = "" + msg_attachment = null + current_message = null + +/datum/nano_module/email_client/proc/relayed_process(var/netspeed) + download_speed = netspeed + if(!downloading) + return + download_progress = min(download_progress + netspeed, downloading.size) + if(download_progress >= downloading.size) + var/obj/item/modular_computer/MC = nano_host() + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" + downloading = null + download_progress = 0 + return 1 + + if(MC.hard_drive.store_file(downloading)) + error = "File successfully downloaded to local device." + else + error = "Error saving file: I/O Error: The hard drive may be full or nonfunctional." + downloading = null + download_progress = 0 + return 1 + + +/datum/nano_module/email_client/Topic(href, href_list) + if(..()) + return 1 + var/mob/living/user = usr + check_for_new_messages(1) // Any actual interaction (button pressing) is considered as acknowledging received message, for the purpose of notification icons. + if(href_list["login"]) + log_in() + return 1 + + if(href_list["logout"]) + log_out() + return 1 + + if(href_list["reset"]) + error = "" + return 1 + + if(href_list["new_message"]) + new_message = TRUE + return 1 + + if(href_list["cancel"]) + if(addressbook) + addressbook = FALSE + else + clear_message() + return 1 + + if(href_list["addressbook"]) + addressbook = TRUE + return 1 + + if(href_list["set_recipient"]) + msg_recipient = sanitize(href_list["set_recipient"]) + addressbook = FALSE + return 1 + + if(href_list["edit_title"]) + var/newtitle = sanitize(input(user,"Enter title for your message:", "Message title", msg_title), 100) + if(newtitle) + msg_title = newtitle + return 1 + + // This uses similar editing mechanism as the FileManager program, therefore it supports various paper tags and remembers formatting. + if(href_list["edit_body"]) + var/oldtext = html_decode(msg_body) + oldtext = replacetext(oldtext, "\[editorbr\]", "\n") + + var/newtext = sanitize(replacetext(input(usr, "Enter your message. You may use most tags from paper formatting", "Message Editor", oldtext) as message|null, "\n", "\[editorbr\]"), 20000) + if(newtext) + msg_body = newtext + return 1 + + if(href_list["edit_recipient"]) + var/newrecipient = sanitize(input(user,"Enter recipient's email address:", "Recipient", msg_recipient), 100) + if(newrecipient) + msg_recipient = newrecipient + return 1 + + if(href_list["edit_login"]) + var/newlogin = sanitize(input(user,"Enter login", "Login", stored_login), 100) + if(newlogin) + stored_login = newlogin + return 1 + + if(href_list["edit_password"]) + var/newpass = sanitize(input(user,"Enter password", "Password"), 100) + if(newpass) + stored_password = newpass + return 1 + + if(href_list["delete"]) + if(!istype(current_account)) + return 1 + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["delete"]) + if(!istype(M)) + return 1 + if(folder == "Deleted") + current_account.deleted.Remove(M) + qdel(M) + else + current_account.deleted.Add(M) + current_account.inbox.Remove(M) + current_account.spam.Remove(M) + if(current_message == M) + current_message = null + return 1 + + if(href_list["send"]) + if(!current_account) + return 1 + if((msg_title == "") || (msg_body == "") || (msg_recipient == "")) + error = "Error sending mail: Title or message body is empty!" + return 1 + + var/datum/computer_file/data/email_message/message = new() + message.title = msg_title + message.stored_data = msg_body + message.source = current_account.login + message.attachment = msg_attachment + if(!current_account.send_mail(msg_recipient, message)) + error = "Error sending email: this address doesn't exist." + return 1 + else + error = "Email successfully sent." + clear_message() + return 1 + + if(href_list["set_folder"]) + folder = href_list["set_folder"] + return 1 + + if(href_list["reply"]) + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["reply"]) + if(!istype(M)) + return 1 + + new_message = TRUE + msg_recipient = M.source + msg_title = "Re: [M.title]" + msg_body = "\[editorbr\]\[editorbr\]\[editorbr\]\[br\]==============================\[br\]\[editorbr\]" + msg_body += "Received by [current_account.login] at [M.timestamp]\[br\]\[editorbr\][M.stored_data]" + return 1 + + if(href_list["view"]) + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["view"]) + if(istype(M)) + current_message = M + return 1 + + if(href_list["changepassword"]) + var/oldpassword = sanitize(input(user,"Please enter your old password:", "Password Change"), 100) + if(!oldpassword) + return 1 + var/newpassword1 = sanitize(input(user,"Please enter your new password:", "Password Change"), 100) + if(!newpassword1) + return 1 + var/newpassword2 = sanitize(input(user,"Please re-enter your new password:", "Password Change"), 100) + if(!newpassword2) + return 1 + + if(!istype(current_account)) + error = "Please log in before proceeding." + return 1 + + if(current_account.password != oldpassword) + error = "Incorrect original password" + return 1 + + if(newpassword1 != newpassword2) + error = "The entered passwords do not match." + return 1 + + current_account.password = newpassword1 + stored_password = newpassword1 + error = "Your password has been successfully changed!" + return 1 + + // The following entries are Modular Computer framework only, and therefore won't do anything in other cases (like AI View) + + if(href_list["save"]) + // Fully dependant on modular computers here. + var/obj/item/modular_computer/MC = nano_host() + + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error exporting file. Are you using a functional and NTOS-compliant device?" + return 1 + + var/filename = sanitize(input(user,"Please specify file name:", "Message export"), 100) + if(!filename) + return 1 + + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["save"]) + var/datum/computer_file/data/mail = istype(M) ? M.export() : null + if(!istype(mail)) + return 1 + mail.filename = filename + if(!MC.hard_drive || !MC.hard_drive.store_file(mail)) + error = "Internal I/O error when writing file, the hard drive may be full." + else + error = "Email exported successfully" + return 1 + + if(href_list["addattachment"]) + var/obj/item/modular_computer/MC = nano_host() + msg_attachment = null + + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" + return 1 + + var/list/filenames = list() + for(var/datum/computer_file/CF in MC.hard_drive.stored_files) + if(CF.unsendable) + continue + filenames.Add(CF.filename) + var/picked_file = input(user, "Please pick a file to send as attachment (max 32GQ)") as null|anything in filenames + + if(!picked_file) + return 1 + + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" + return 1 + + for(var/datum/computer_file/CF in MC.hard_drive.stored_files) + if(CF.unsendable) + continue + if(CF.filename == picked_file) + msg_attachment = CF.clone() + break + if(!istype(msg_attachment)) + msg_attachment = null + error = "Unknown error when uploading attachment." + return 1 + + if(msg_attachment.size > 32) + error = "Error uploading attachment: File exceeds maximal permitted file size of 32GQ." + msg_attachment = null + else + error = "File [msg_attachment.filename].[msg_attachment.filetype] has been successfully uploaded." + return 1 + + if(href_list["downloadattachment"]) + if(!current_account || !current_message || !current_message.attachment) + return 1 + var/obj/item/modular_computer/MC = nano_host() + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error downloading file. Are you using a functional and NTOSv2-compliant device?" + return 1 + + downloading = current_message.attachment.clone() + download_progress = 0 + return 1 + + if(href_list["canceldownload"]) + downloading = null + download_progress = 0 + return 1 + + if(href_list["remove_attachment"]) + msg_attachment = null + return 1 \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm new file mode 100644 index 0000000000..42dfdd622d --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -0,0 +1,207 @@ +/datum/computer_file/program/filemanager + filename = "filemanager" + filedesc = "NTOS File Manager" + extended_desc = "This program allows management of files." + program_icon_state = "generic" + program_key_state = "generic_key" + program_menu_icon = "folder-collapsed" + size = 8 + requires_ntnet = 0 + available_on_ntnet = 0 + undeletable = 1 + nanomodule_path = /datum/nano_module/program/computer_filemanager/ + var/open_file + var/error + +/datum/computer_file/program/filemanager/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["PRG_openfile"]) + . = 1 + open_file = href_list["PRG_openfile"] + if(href_list["PRG_newtextfile"]) + . = 1 + var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename")) + if(!newname) + return 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/data/F = new/datum/computer_file/data() + F.filename = newname + F.filetype = "TXT" + HDD.store_file(F) + if(href_list["PRG_deletefile"]) + . = 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_deletefile"]) + if(!file || file.undeletable) + return 1 + HDD.remove_file(file) + if(href_list["PRG_usbdeletefile"]) + . = 1 + var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive + if(!RHDD) + return 1 + var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_usbdeletefile"]) + if(!file || file.undeletable) + return 1 + RHDD.remove_file(file) + if(href_list["PRG_closefile"]) + . = 1 + open_file = null + error = null + if(href_list["PRG_clone"]) + . = 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_clone"]) + if(!F || !istype(F)) + return 1 + var/datum/computer_file/C = F.clone(1) + HDD.store_file(C) + if(href_list["PRG_rename"]) + . = 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_rename"]) + if(!file || !istype(file)) + return 1 + var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename)) + if(file && newname) + file.filename = newname + if(href_list["PRG_edit"]) + . = 1 + if(!open_file) + return 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) + if(!F || !istype(F)) + return 1 + if(F.do_not_edit && (alert("WARNING: This file is not compatible with editor. Editing it may result in permanently corrupted formatting or damaged data consistency. Edit anyway?", "Incompatible File", "No", "Yes") == "No")) + return 1 + + var/oldtext = html_decode(F.stored_data) + oldtext = replacetext(oldtext, "\[br\]", "\n") + + var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) + if(!newtext) + return + + if(F) + var/datum/computer_file/data/backup = F.clone() + HDD.remove_file(F) + F.stored_data = newtext + F.calculate_size() + // We can't store the updated file, it's probably too large. Print an error and restore backed up version. + // This is mostly intended to prevent people from losing texts they spent lot of time working on due to running out of space. + // They will be able to copy-paste the text from error screen and store it in notepad or something. + if(!HDD.store_file(F)) + error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:

      [html_decode(F.stored_data)]

      " + HDD.store_file(backup) + if(href_list["PRG_printfile"]) + . = 1 + if(!open_file) + return 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) + if(!F || !istype(F)) + return 1 + if(!computer.nano_printer) + error = "Missing Hardware: Your computer does not have required hardware to complete this operation." + return 1 + if(!computer.nano_printer.print_text(pencode2html(F.stored_data))) + error = "Hardware error: Printer was unable to print the file. It may be out of paper." + return 1 + if(href_list["PRG_copytousb"]) + . = 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive + if(!HDD || !RHDD) + return 1 + var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_copytousb"]) + if(!F || !istype(F)) + return 1 + var/datum/computer_file/C = F.clone(0) + RHDD.store_file(C) + if(href_list["PRG_copyfromusb"]) + . = 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive + if(!HDD || !RHDD) + return 1 + var/datum/computer_file/F = RHDD.find_file_by_name(href_list["PRG_copyfromusb"]) + if(!F || !istype(F)) + return 1 + var/datum/computer_file/C = F.clone(0) + HDD.store_file(C) + if(.) + SSnanoui.update_uis(NM) + +/datum/nano_module/program/computer_filemanager + name = "NTOS File Manager" + +/datum/nano_module/program/computer_filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + var/datum/computer_file/program/filemanager/PRG + PRG = program + + var/obj/item/weapon/computer_hardware/hard_drive/HDD + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD + if(PRG.error) + data["error"] = PRG.error + if(PRG.open_file) + var/datum/computer_file/data/file + + if(!PRG.computer || !PRG.computer.hard_drive) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + HDD = PRG.computer.hard_drive + file = HDD.find_file_by_name(PRG.open_file) + if(!istype(file)) + data["error"] = "I/O ERROR: Unable to open file." + else + data["filedata"] = pencode2html(file.stored_data) + data["filename"] = "[file.filename].[file.filetype]" + else + if(!PRG.computer || !PRG.computer.hard_drive) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + HDD = PRG.computer.hard_drive + RHDD = PRG.computer.portable_drive + var/list/files[0] + for(var/datum/computer_file/F in HDD.stored_files) + files.Add(list(list( + "name" = F.filename, + "type" = F.filetype, + "size" = F.size, + "undeletable" = F.undeletable + ))) + data["files"] = files + if(RHDD) + data["usbconnected"] = 1 + var/list/usbfiles[0] + for(var/datum/computer_file/F in RHDD.stored_files) + usbfiles.Add(list(list( + "name" = F.filename, + "type" = F.filetype, + "size" = F.size, + "undeletable" = F.undeletable + ))) + data["usbfiles"] = usbfiles + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/game.dm b/code/modules/modular_computers/file_system/programs/generic/game.dm new file mode 100644 index 0000000000..b4c0688c29 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/game.dm @@ -0,0 +1,152 @@ +// This file is used as a reference for Modular Computers Development guide on the wiki. It contains a lot of excess comments, as it is intended as explanation +// for someone who may not be as experienced in coding. When making changes, please try to keep it this way. + +// An actual program definition. +/datum/computer_file/program/game + filename = "arcadec" // File name, as shown in the file browser program. + filedesc = "Unknown Game" // User-Friendly name. In this case, we will generate a random name in constructor. + program_icon_state = "game" // Icon state of this program's screen. + program_menu_icon = "script" + extended_desc = "Fun for the whole family! Probably not an AAA title, but at least you can download it on the corporate network.." // A nice description. + size = 5 // Size in GQ. Integers only. Smaller sizes should be used for utility/low use programs (like this one), while large sizes are for important programs. + requires_ntnet = 0 // This particular program does not require NTNet network conectivity... + available_on_ntnet = 1 // ... but we want it to be available for download. + nanomodule_path = /datum/nano_module/arcade_classic/ // Path of relevant nano module. The nano module is defined further in the file. + var/picked_enemy_name + +// Blatantly stolen and shortened version from arcade machines. Generates a random enemy name +/datum/computer_file/program/game/proc/random_enemy_name() + var/name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") + var/name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "Slime", "Lizard Man", "Unicorn") + return "[name_part1] [name_part2]" + +// When the program is first created, we generate a new enemy name and name ourselves accordingly. +/datum/computer_file/program/game/New() + ..() + picked_enemy_name = random_enemy_name() + filedesc = "Defeat [picked_enemy_name]" + +// Important in order to ensure that copied versions will have the same enemy name. +/datum/computer_file/program/game/clone() + var/datum/computer_file/program/game/G = ..() + G.picked_enemy_name = picked_enemy_name + return G + +// When running the program, we also want to pass our enemy name to the nano module. +/datum/computer_file/program/game/run_program() + . = ..() + if(. && NM) + var/datum/nano_module/arcade_classic/NMC = NM + NMC.enemy_name = picked_enemy_name + + +// Nano module the program uses. +// This can be either /datum/nano_module/ or /datum/nano_module/program. The latter is intended for nano modules that are suposed to be exclusively used with modular computers, +// and should generally not be used, as such nano modules are hard to use on other places. +/datum/nano_module/arcade_classic/ + name = "Classic Arcade" + var/player_mana // Various variables specific to the nano module. In this case, the nano module is a simple arcade game, so the variables store health and other stats. + var/player_health + var/enemy_mana + var/enemy_health + var/enemy_name = "Greytide Horde" + var/gameover + var/information + +/datum/nano_module/arcade_classic/New() + ..() + new_game() + +// ui_interact handles transfer of data to NanoUI. Keep in mind that data you pass from here is actually sent to the client. In other words, don't send anything you don't want a client +// to see, and don't send unnecessarily large amounts of data (due to laginess). +/datum/nano_module/arcade_classic/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + data["player_health"] = player_health + data["player_mana"] = player_mana + data["enemy_health"] = enemy_health + data["enemy_mana"] = enemy_mana + data["enemy_name"] = enemy_name + data["gameover"] = gameover + data["information"] = information + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "arcade_classic.tmpl", "Defeat [enemy_name]", 500, 350, state = state) + if(host.update_layout()) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + +// Three helper procs i've created. These are unique to this particular nano module. If you are creating your own nano module, you'll most likely create similar procs too. +/datum/nano_module/arcade_classic/proc/enemy_play() + if((enemy_mana < 5) && prob(60)) + var/steal = rand(2, 3) + player_mana -= steal + enemy_mana += steal + information += "[enemy_name] steals [steal] of your power!" + else if((enemy_health < 15) && (enemy_mana > 3) && prob(80)) + var/healamt = min(rand(3, 5), enemy_mana) + enemy_mana -= healamt + enemy_health += healamt + information += "[enemy_name] heals for [healamt] health!" + else + var/dam = rand(3,6) + player_health -= dam + information += "[enemy_name] attacks for [dam] damage!" + +/datum/nano_module/arcade_classic/proc/check_gameover() + if((player_health <= 0) || player_mana <= 0) + if(enemy_health <= 0) + information += "You have defeated [enemy_name], but you have died in the fight!" + else + information += "You have been defeated by [enemy_name]!" + gameover = 1 + return TRUE + else if(enemy_health <= 0) + gameover = 1 + information += "Congratulations! You have defeated [enemy_name]!" + return TRUE + return FALSE + +/datum/nano_module/arcade_classic/proc/new_game() + player_mana = 10 + player_health = 30 + enemy_mana = 20 + enemy_health = 45 + gameover = FALSE + information = "A new game has started!" + + + +/datum/nano_module/arcade_classic/Topic(href, href_list) + if(..()) // Always begin your Topic() calls with a parent call! + return 1 + if(href_list["new_game"]) + new_game() + return 1 // Returning 1 (TRUE) in Topic automatically handles UI updates. + if(gameover) // If the game has already ended, we don't want the following three topic calls to be processed at all. + return 1 // Instead of adding checks into each of those three, we can easily add this one check here to reduce on code copy-paste. + if(href_list["attack"]) + var/damage = rand(2, 6) + information = "You attack for [damage] damage." + enemy_health -= damage + enemy_play() + check_gameover() + return 1 + if(href_list["heal"]) + var/healfor = rand(6, 8) + var/cost = rand(1, 3) + information = "You heal yourself for [healfor] damage, using [cost] energy in the process." + player_health += healfor + player_mana -= cost + enemy_play() + check_gameover() + return 1 + if(href_list["regain_mana"]) + var/regen = rand(4, 7) + information = "You rest of a while, regaining [regen] energy." + player_mana += regen + enemy_play() + check_gameover() + return 1 \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/news_browser.dm b/code/modules/modular_computers/file_system/programs/generic/news_browser.dm new file mode 100644 index 0000000000..1d4da6d4d5 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/news_browser.dm @@ -0,0 +1,131 @@ +/datum/computer_file/program/newsbrowser + filename = "newsbrowser" + filedesc = "NTNet/ExoNet News Browser" + extended_desc = "This program may be used to view and download news articles from the network." + program_icon_state = "generic" + program_key_state = "generic_key" + program_menu_icon = "contact" + size = 4 + requires_ntnet = 1 + available_on_ntnet = 1 + + nanomodule_path = /datum/nano_module/program/computer_newsbrowser/ + var/datum/computer_file/data/news_article/loaded_article + var/download_progress = 0 + var/download_netspeed = 0 + var/downloading = 0 + var/message = "" + var/show_archived = 0 + +/datum/computer_file/program/newsbrowser/process_tick() + if(!downloading) + return + download_netspeed = 0 + // Speed defines are found in misc.dm + switch(ntnet_status) + if(1) + download_netspeed = NTNETSPEED_LOWSIGNAL + if(2) + download_netspeed = NTNETSPEED_HIGHSIGNAL + if(3) + download_netspeed = NTNETSPEED_ETHERNET + download_progress += download_netspeed + if(download_progress >= loaded_article.size) + downloading = 0 + requires_ntnet = 0 // Turn off NTNet requirement as we already loaded the file into local memory. + SSnanoui.update_uis(NM) + +/datum/computer_file/program/newsbrowser/kill_program() + ..() + requires_ntnet = 1 + loaded_article = null + download_progress = 0 + downloading = 0 + show_archived = 0 + +/datum/computer_file/program/newsbrowser/Topic(href, href_list) + if(..()) + return 1 + if(href_list["PRG_openarticle"]) + . = 1 + if(downloading || loaded_article) + return 1 + + for(var/datum/computer_file/data/news_article/N in ntnet_global.available_news) + if(N.uid == text2num(href_list["PRG_openarticle"])) + loaded_article = N.clone() + downloading = 1 + break + if(href_list["PRG_reset"]) + . = 1 + downloading = 0 + download_progress = 0 + requires_ntnet = 1 + loaded_article = null + if(href_list["PRG_clearmessage"]) + . = 1 + message = "" + if(href_list["PRG_savearticle"]) + . = 1 + if(downloading || !loaded_article) + return + + var/savename = sanitize(input(usr, "Enter file name or leave blank to cancel:", "Save article", loaded_article.filename)) + if(!savename) + return 1 + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return 1 + var/datum/computer_file/data/news_article/N = loaded_article.clone() + N.filename = savename + HDD.store_file(N) + if(href_list["PRG_toggle_archived"]) + . = 1 + show_archived = !show_archived + if(.) + SSnanoui.update_uis(NM) + + +/datum/nano_module/program/computer_newsbrowser + name = "NTNet/ExoNet News Browser" + +/datum/nano_module/program/computer_newsbrowser/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + + var/datum/computer_file/program/newsbrowser/PRG + var/list/data = list() + if(program) + data = program.get_header_data() + PRG = program + else + return + + data["message"] = PRG.message + if(PRG.loaded_article && !PRG.downloading) // Viewing an article. + data["title"] = PRG.loaded_article.filename + data["cover"] = PRG.loaded_article.cover + data["article"] = PRG.loaded_article.stored_data + else if(PRG.downloading) // Downloading an article. + data["download_running"] = 1 + data["download_progress"] = PRG.download_progress + data["download_maxprogress"] = PRG.loaded_article.size + data["download_rate"] = PRG.download_netspeed + else // Viewing list of articles + var/list/all_articles[0] + for(var/datum/computer_file/data/news_article/F in ntnet_global.available_news) + if(!PRG.show_archived && F.archived) + continue + all_articles.Add(list(list( + "name" = F.filename, + "size" = F.size, + "uid" = F.uid, + "archived" = F.archived + ))) + data["all_articles"] = all_articles + data["showing_archived"] = PRG.show_archived + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "news_browser.tmpl", "NTNet/ExoNet News Browser", 575, 750, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm new file mode 100644 index 0000000000..a0b2194168 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -0,0 +1,200 @@ +/datum/computer_file/program/ntnetdownload + filename = "ntndownloader" + filedesc = "NTNet Software Download Tool" + program_icon_state = "generic" + program_key_state = "generic_key" + program_menu_icon = "arrowthickstop-1-s" + extended_desc = "This program allows downloads of software from official NT repositories" + unsendable = 1 + undeletable = 1 + size = 4 + requires_ntnet = 1 + requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD + available_on_ntnet = 0 + nanomodule_path = /datum/nano_module/program/computer_ntnetdownload/ + ui_header = "downloader_finished.gif" + var/datum/computer_file/program/downloaded_file = null + var/hacked_download = 0 + var/download_completion = 0 //GQ of downloaded data. + var/download_netspeed = 0 + var/downloaderror = "" + var/list/downloads_queue[0] + +/datum/computer_file/program/ntnetdownload/kill_program() + ..() + downloaded_file = null + download_completion = 0 + download_netspeed = 0 + downloaderror = "" + ui_header = "downloader_finished.gif" + + +/datum/computer_file/program/ntnetdownload/proc/begin_file_download(var/filename) + if(downloaded_file) + return 0 + + var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename) + + if(!check_file_download(filename)) + return 0 + + ui_header = "downloader_running.gif" + + if(PRG in ntnet_global.available_station_software) + generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.") + hacked_download = 0 + else if(PRG in ntnet_global.available_antag_software) + generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.") + hacked_download = 1 + else + generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from unspecified server.") + hacked_download = 0 + + downloaded_file = PRG.clone() + +/datum/computer_file/program/ntnetdownload/proc/check_file_download(var/filename) + //returns 1 if file can be downloaded, returns 0 if download prohibited + var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename) + + if(!PRG || !istype(PRG)) + return 0 + + // Attempting to download antag only program, but without having emagged computer. No. + if(PRG.available_on_syndinet && !computer_emagged) + return 0 + + if(!computer || !computer.hard_drive || !computer.hard_drive.try_store_file(PRG)) + return 0 + + return 1 + +/datum/computer_file/program/ntnetdownload/proc/abort_file_download() + if(!downloaded_file) + return + generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].") + downloaded_file = null + download_completion = 0 + ui_header = "downloader_finished.gif" + +/datum/computer_file/program/ntnetdownload/proc/complete_file_download() + if(!downloaded_file) + return + generate_network_log("Completed download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].") + if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(downloaded_file)) + // The download failed + downloaderror = "I/O ERROR - Unable to save file. Check whether you have enough free space on your hard drive and whether your hard drive is properly connected. If the issue persists contact your system administrator for assistance." + downloaded_file = null + download_completion = 0 + ui_header = "downloader_finished.gif" + +/datum/computer_file/program/ntnetdownload/process_tick() + if(!downloaded_file) + return + if(download_completion >= downloaded_file.size) + complete_file_download() + if(downloads_queue.len > 0) + begin_file_download(downloads_queue[1]) + downloads_queue.Remove(downloads_queue[1]) + + // Download speed according to connectivity state. NTNet server is assumed to be on unlimited speed so we're limited by our local connectivity + download_netspeed = 0 + // Speed defines are found in misc.dm + switch(ntnet_status) + if(1) + download_netspeed = NTNETSPEED_LOWSIGNAL + if(2) + download_netspeed = NTNETSPEED_HIGHSIGNAL + if(3) + download_netspeed = NTNETSPEED_ETHERNET + download_completion += download_netspeed + +/datum/computer_file/program/ntnetdownload/Topic(href, href_list) + if(..()) + return 1 + if(href_list["PRG_downloadfile"]) + if(!downloaded_file) + begin_file_download(href_list["PRG_downloadfile"]) + else if(check_file_download(href_list["PRG_downloadfile"]) && !downloads_queue.Find(href_list["PRG_downloadfile"]) && downloaded_file.filename != href_list["PRG_downloadfile"]) + downloads_queue += href_list["PRG_downloadfile"] + return 1 + if(href_list["PRG_removequeued"]) + downloads_queue.Remove(href_list["PRG_removequeued"]) + return 1 + if(href_list["PRG_reseterror"]) + if(downloaderror) + download_completion = 0 + download_netspeed = 0 + downloaded_file = null + downloaderror = "" + return 1 + return 0 + +/datum/nano_module/program/computer_ntnetdownload + name = "Network Downloader" + var/obj/item/modular_computer/my_computer = null + +/datum/nano_module/program/computer_ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(program) + my_computer = program.computer + + if(!istype(my_computer)) + return + + var/list/data = list() + var/datum/computer_file/program/ntnetdownload/prog = program + // For now limited to execution by the downloader program + if(!prog || !istype(prog)) + return + if(program) + data = program.get_header_data() + + // This IF cuts on data transferred to client, so i guess it's worth it. + if(prog.downloaderror) // Download errored. Wait until user resets the program. + data["error"] = prog.downloaderror + if(prog.downloaded_file) // Download running. Wait please.. + data["downloadname"] = prog.downloaded_file.filename + data["downloaddesc"] = prog.downloaded_file.filedesc + data["downloadsize"] = prog.downloaded_file.size + data["downloadspeed"] = prog.download_netspeed + data["downloadcompletion"] = round(prog.download_completion, 0.1) + + data["disk_size"] = my_computer.hard_drive.max_capacity + data["disk_used"] = my_computer.hard_drive.used_capacity + var/list/all_entries[0] + for(var/datum/computer_file/program/P in ntnet_global.available_station_software) + // Only those programs our user can run will show in the list + if(!P.can_run(user) && P.requires_access_to_download) + continue + all_entries.Add(list(list( + "filename" = P.filename, + "filedesc" = P.filedesc, + "fileinfo" = P.extended_desc, + "size" = P.size, + "icon" = P.program_menu_icon + ))) + data["hackedavailable"] = 0 + if(prog.computer_emagged) // If we are running on emagged computer we have access to some "bonus" software + var/list/hacked_programs[0] + for(var/datum/computer_file/program/P in ntnet_global.available_antag_software) + data["hackedavailable"] = 1 + hacked_programs.Add(list(list( + "filename" = P.filename, + "filedesc" = P.filedesc, + "fileinfo" = P.extended_desc, + "size" = P.size, + "icon" = P.program_menu_icon + ))) + data["hacked_programs"] = hacked_programs + + data["downloadable_programs"] = all_entries + + if(prog.downloads_queue.len > 0) + data["downloads_queue"] = prog.downloads_queue + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm new file mode 100644 index 0000000000..6c67c02c0d --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm @@ -0,0 +1,232 @@ +/datum/computer_file/program/chatclient + filename = "ntnrc_client" + filedesc = "NTNet Relay Chat Client" + program_icon_state = "command" + program_key_state = "med_key" + program_menu_icon = "comment" + extended_desc = "This program allows communication over NTNRC network" + size = 8 + requires_ntnet = 1 + requires_ntnet_feature = NTNET_COMMUNICATION + network_destination = "NTNRC server" + ui_header = "ntnrc_idle.gif" + available_on_ntnet = 1 + nanomodule_path = /datum/nano_module/program/computer_chatclient/ + var/last_message = null // Used to generate the toolbar icon + var/username + var/datum/ntnet_conversation/channel = null + var/operator_mode = 0 // Channel operator mode + var/netadmin_mode = 0 // Administrator mode (invisible to other users + bypasses passwords) + +/datum/computer_file/program/chatclient/New() + username = "DefaultUser[rand(100, 999)]" + +/datum/computer_file/program/chatclient/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["PRG_speak"]) + . = 1 + if(!channel) + return 1 + var/mob/living/user = usr + var/message = sanitize(input(user, "Enter message or leave blank to cancel: "), 512) + if(!message || !channel) + return + channel.add_message(message, username) + + if(href_list["PRG_joinchannel"]) + . = 1 + var/datum/ntnet_conversation/C + for(var/datum/ntnet_conversation/chan in ntnet_global.chat_channels) + if(chan.id == text2num(href_list["PRG_joinchannel"])) + C = chan + break + + if(!C) + return 1 + + if(netadmin_mode) + channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. + return 1 + + if(C.password) + var/mob/living/user = usr + var/password = sanitize(input(user,"Access Denied. Enter password:")) + if(C && (password == C.password)) + C.add_client(src) + channel = C + return 1 + C.add_client(src) + channel = C + if(href_list["PRG_leavechannel"]) + . = 1 + if(channel) + channel.remove_client(src) + channel = null + if(href_list["PRG_newchannel"]) + . = 1 + var/mob/living/user = usr + var/channel_title = sanitizeSafe(input(user,"Enter channel name or leave blank to cancel:"), 64) + if(!channel_title) + return + var/datum/ntnet_conversation/C = new/datum/ntnet_conversation() + C.add_client(src) + C.operator = src + channel = C + C.title = channel_title + if(href_list["PRG_toggleadmin"]) + . = 1 + if(netadmin_mode) + netadmin_mode = 0 + if(channel) + channel.remove_client(src) // We shouldn't be in channel's user list, but just in case... + channel = null + return 1 + var/mob/living/user = usr + if(can_run(usr, 1, access_network)) + if(channel) + var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") + if(response == "Yes") + if(channel) + channel.remove_client(src) + channel = null + else + return + netadmin_mode = 1 + if(href_list["PRG_changename"]) + . = 1 + var/mob/living/user = usr + var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:"), 20) + if(!newname) + return 1 + if(channel) + channel.add_status_message("[username] is now known as [newname].") + username = newname + + if(href_list["PRG_savelog"]) + . = 1 + if(!channel) + return + var/mob/living/user = usr + var/logname = input(user,"Enter desired logfile name (.log) or leave blank to cancel:") + if(!logname || !channel) + return 1 + var/datum/computer_file/data/logfile = new/datum/computer_file/data/logfile() + // Now we will generate HTML-compliant file that can actually be viewed/printed. + logfile.filename = logname + logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]" + for(var/logstring in channel.messages) + logfile.stored_data += "[logstring]\[BR\]" + logfile.stored_data += "\[b\]Logfile dump completed.\[/b\]" + logfile.calculate_size() + if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(logfile)) + if(!computer) + // This program shouldn't even be runnable without computer. + CRASH("Var computer is null!") + return 1 + if(!computer.hard_drive) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") + else // In 99.9% cases this will mean our HDD is full + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + if(href_list["PRG_renamechannel"]) + . = 1 + if(!operator_mode || !channel) + return 1 + var/mob/living/user = usr + var/newname = sanitize(input(user, "Enter new channel name or leave blank to cancel:"), 64) + if(!newname || !channel) + return + channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") + channel.title = newname + if(href_list["PRG_deletechannel"]) + . = 1 + if(channel && ((channel.operator == src) || netadmin_mode)) + qdel(channel) + channel = null + if(href_list["PRG_setpassword"]) + . = 1 + if(!channel || ((channel.operator != src) && !netadmin_mode)) + return 1 + + var/mob/living/user = usr + var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:")) + if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode)) + return 1 + + if(newpassword == "nopassword") + channel.password = "" + else + channel.password = newpassword + +/datum/computer_file/program/chatclient/process_tick() + ..() + if(program_state != PROGRAM_STATE_KILLED) + ui_header = "ntnrc_idle.gif" + if(channel) + // Remember the last message. If there is no message in the channel remember null. + last_message = channel.messages.len ? channel.messages[channel.messages.len - 1] : null + else + last_message = null + return 1 + if(channel && channel.messages && channel.messages.len) + ui_header = last_message == channel.messages[channel.messages.len - 1] ? "ntnrc_idle.gif" : "ntnrc_new.gif" + else + ui_header = "ntnrc_idle.gif" + +/datum/computer_file/program/chatclient/kill_program(var/forced = 0) + if(channel) + channel.remove_client(src) + channel = null + ..(forced) + +/datum/nano_module/program/computer_chatclient + name = "NTNet Relay Chat Client" + +/datum/nano_module/program/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!ntnet_global || !ntnet_global.chat_channels) + return + + var/list/data = list() + if(program) + data = program.get_header_data() + + var/datum/computer_file/program/chatclient/C = program + if(!istype(C)) + return + + data["adminmode"] = C.netadmin_mode + if(C.channel) + data["title"] = C.channel.title + var/list/messages[0] + for(var/M in C.channel.messages) + messages.Add(list(list( + "msg" = M + ))) + data["messages"] = messages + var/list/clients[0] + for(var/datum/computer_file/program/chatclient/cl in C.channel.clients) + clients.Add(list(list( + "name" = cl.username + ))) + data["clients"] = clients + C.operator_mode = (C.channel.operator == C) ? 1 : 0 + data["is_operator"] = C.operator_mode || C.netadmin_mode + + else // Channel selection screen + var/list/all_channels[0] + for(var/datum/ntnet_conversation/conv in ntnet_global.chat_channels) + if(conv && conv.title) + all_channels.Add(list(list( + "chan" = conv.title, + "id" = conv.id + ))) + data["all_channels"] = all_channels + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) diff --git a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm new file mode 100644 index 0000000000..0efed986ae --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm @@ -0,0 +1,185 @@ +var/global/nttransfer_uid = 0 + +/datum/computer_file/program/nttransfer + filename = "nttransfer" + filedesc = "NTNet P2P Transfer Client" + extended_desc = "This program allows for simple file transfer via direct peer to peer connection." + program_icon_state = "comm_logs" + program_key_state = "generic_key" + program_menu_icon = "transferthick-e-w" + size = 7 + requires_ntnet = 1 + requires_ntnet_feature = NTNET_PEERTOPEER + network_destination = "other device via P2P tunnel" + available_on_ntnet = 1 + nanomodule_path = /datum/nano_module/program/computer_nttransfer/ + + var/error = "" // Error screen + var/server_password = "" // Optional password to download the file. + var/datum/computer_file/provided_file = null // File which is provided to clients. + var/datum/computer_file/downloaded_file = null // File which is being downloaded + var/list/connected_clients = list() // List of connected clients. + var/datum/computer_file/program/nttransfer/remote // Client var, specifies who are we downloading from. + var/download_completion = 0 // Download progress in GQ + var/actual_netspeed = 0 // Displayed in the UI, this is the actual transfer speed. + var/unique_token // UID of this program + var/upload_menu = 0 // Whether we show the program list and upload menu + +/datum/computer_file/program/nttransfer/New() + unique_token = nttransfer_uid + nttransfer_uid++ + ..() + +/datum/computer_file/program/nttransfer/process_tick() + ..() + // Server mode + if(provided_file) + for(var/datum/computer_file/program/nttransfer/C in connected_clients) + // Transfer speed is limited by device which uses slower connectivity. + // We can have multiple clients downloading at same time, but let's assume we use some sort of multicast transfer + // so they can all run on same speed. + C.actual_netspeed = min(C.ntnet_speed, ntnet_speed) + C.download_completion += C.actual_netspeed + if(C.download_completion >= provided_file.size) + C.finish_download() + else if(downloaded_file) // Client mode + if(!remote) + crash_download("Connection to remote server lost") + +/datum/computer_file/program/nttransfer/kill_program(var/forced = 0) + if(downloaded_file) // Client mode, clean up variables for next use + finalize_download() + + if(provided_file) // Server mode, disconnect all clients + for(var/datum/computer_file/program/nttransfer/P in connected_clients) + P.crash_download("Connection terminated by remote server") + downloaded_file = null + ..(forced) + +// Finishes download and attempts to store the file on HDD +/datum/computer_file/program/nttransfer/proc/finish_download() + if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(downloaded_file)) + error = "I/O Error: Unable to save file. Check your hard drive and try again." + finalize_download() + +// Crashes the download and displays specific error message +/datum/computer_file/program/nttransfer/proc/crash_download(var/message) + error = message ? message : "An unknown error has occured during download" + finalize_download() + +// Cleans up variables for next use +/datum/computer_file/program/nttransfer/proc/finalize_download() + if(remote) + remote.connected_clients.Remove(src) + downloaded_file = null + remote = null + download_completion = 0 + + +/datum/nano_module/program/computer_nttransfer + name = "NTNet P2P Transfer Client" + +/datum/nano_module/program/computer_nttransfer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!program) + return + var/datum/computer_file/program/nttransfer/PRG = program + if(!istype(PRG)) + return + + var/list/data = program.get_header_data() + + if(PRG.error) + data["error"] = PRG.error + else if(PRG.downloaded_file) + data["downloading"] = 1 + data["download_size"] = PRG.downloaded_file.size + data["download_progress"] = PRG.download_completion + data["download_netspeed"] = PRG.actual_netspeed + data["download_name"] = "[PRG.downloaded_file.filename].[PRG.downloaded_file.filetype]" + else if (PRG.provided_file) + data["uploading"] = 1 + data["upload_uid"] = PRG.unique_token + data["upload_clients"] = PRG.connected_clients.len + data["upload_haspassword"] = PRG.server_password ? 1 : 0 + data["upload_filename"] = "[PRG.provided_file.filename].[PRG.provided_file.filetype]" + else if (PRG.upload_menu) + var/list/all_files[0] + for(var/datum/computer_file/F in PRG.computer.hard_drive.stored_files) + all_files.Add(list(list( + "uid" = F.uid, + "filename" = "[F.filename].[F.filetype]", + "size" = F.size + ))) + data["upload_filelist"] = all_files + else + var/list/all_servers[0] + for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) + if(!P.provided_file) + continue + all_servers.Add(list(list( + "uid" = P.unique_token, + "filename" = "[P.provided_file.filename].[P.provided_file.filetype]", + "size" = P.provided_file.size, + "haspassword" = P.server_password ? 1 : 0 + ))) + data["servers"] = all_servers + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_transfer.tmpl", "NTNet P2P Transfer Client", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/computer_file/program/nttransfer/Topic(href, href_list) + if(..()) + return 1 + if(href_list["PRG_downloadfile"]) + for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) + if("[P.unique_token]" == href_list["PRG_downloadfile"]) + remote = P + break + if(!remote || !remote.provided_file) + return + if(remote.server_password) + var/pass = sanitize(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required")) + if(pass != remote.server_password) + error = "Incorrect Password" + return + downloaded_file = remote.provided_file.clone() + remote.connected_clients.Add(src) + return 1 + if(href_list["PRG_reset"]) + error = "" + upload_menu = 0 + finalize_download() + if(src in ntnet_global.fileservers) + ntnet_global.fileservers.Remove(src) + for(var/datum/computer_file/program/nttransfer/T in connected_clients) + T.crash_download("Remote server has forcibly closed the connection") + provided_file = null + return 1 + if(href_list["PRG_setpassword"]) + var/pass = sanitize(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none")) + if(!pass) + return + if(pass == "none") + server_password = "" + return + server_password = pass + return 1 + if(href_list["PRG_uploadfile"]) + for(var/datum/computer_file/F in computer.hard_drive.stored_files) + if("[F.uid]" == href_list["PRG_uploadfile"]) + if(F.unsendable) + error = "I/O Error: File locked." + return + provided_file = F + ntnet_global.fileservers.Add(src) + return + error = "I/O Error: Unable to locate file on hard drive." + return 1 + if(href_list["PRG_uploadmenu"]) + upload_menu = 1 + return 0 diff --git a/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm b/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm new file mode 100644 index 0000000000..1a4d7a1871 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm @@ -0,0 +1,235 @@ +/datum/computer_file/program/wordprocessor + filename = "wordprocessor" + filedesc = "NanoWord" + extended_desc = "This program allows the editing and preview of text documents." + program_icon_state = "word" + program_key_state = "atmos_key" + size = 4 + requires_ntnet = 0 + available_on_ntnet = 1 + nanomodule_path = /datum/nano_module/program/computer_wordprocessor/ + var/browsing + var/open_file + var/loaded_data + var/error + var/is_edited + +/datum/computer_file/program/wordprocessor/proc/get_file(var/filename) + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return + var/datum/computer_file/data/F = HDD.find_file_by_name(filename) + if(!istype(F)) + return + return F + +/datum/computer_file/program/wordprocessor/proc/open_file(var/filename) + var/datum/computer_file/data/F = get_file(filename) + if(F) + open_file = F.filename + loaded_data = F.stored_data + return 1 + +/datum/computer_file/program/wordprocessor/proc/save_file(var/filename) + var/datum/computer_file/data/F = get_file(filename) + if(!F) //try to make one if it doesn't exist + F = create_file(filename, loaded_data) + return !isnull(F) + var/datum/computer_file/data/backup = F.clone() + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return + HDD.remove_file(F) + F.stored_data = loaded_data + F.calculate_size() + if(!HDD.store_file(F)) + HDD.store_file(backup) + return 0 + is_edited = 0 + return 1 + +/datum/computer_file/program/wordprocessor/proc/create_file(var/newname, var/data = "") + if(!newname) + return + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return + if(get_file(newname)) + return + var/datum/computer_file/data/F = new/datum/computer_file/data() + F.filename = newname + F.filetype = "TXT" + F.stored_data = data + F.calculate_size() + if(HDD.store_file(F)) + return F + +/datum/computer_file/program/wordprocessor/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["PRG_txtrpeview"]) + show_browser(usr,"[open_file][pencode2html(loaded_data)]", "window=[open_file]") + return 1 + + if(href_list["PRG_taghelp"]) + to_chat(usr, "The hologram of a googly-eyed paper clip helpfully tells you:") + var/help = {" + \[br\] : Creates a linebreak. + \[center\] - \[/center\] : Centers the text. + \[h1\] - \[/h1\] : First level heading. + \[h2\] - \[/h2\] : Second level heading. + \[h3\] - \[/h3\] : Third level heading. + \[b\] - \[/b\] : Bold. + \[i\] - \[/i\] : Italic. + \[u\] - \[/u\] : Underlined. + \[small\] - \[/small\] : Decreases the size of the text. + \[large\] - \[/large\] : Increases the size of the text. + \[field\] : Inserts a blank text field, which can be filled later. Useful for forms. + \[date\] : Current station date. + \[time\] : Current station time. + \[list\] - \[/list\] : Begins and ends a list. + \[*\] : A list item. + \[hr\] : Horizontal rule. + \[table\] - \[/table\] : Creates table using \[row\] and \[cell\] tags. + \[grid\] - \[/grid\] : Table without visible borders, for layouts. + \[row\] - New table row. + \[cell\] - New table cell. + \[logo\] - Inserts NT logo image. + \[redlogo\] - Inserts red NT logo image. + \[sglogo\] - Inserts Solgov insignia image."} + + to_chat(usr, help) + return 1 + + if(href_list["PRG_closebrowser"]) + browsing = 0 + return 1 + + if(href_list["PRG_backtomenu"]) + error = null + return 1 + + if(href_list["PRG_loadmenu"]) + browsing = 1 + return 1 + + if(href_list["PRG_openfile"]) + . = 1 + if(is_edited) + if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") + save_file(open_file) + browsing = 0 + if(!open_file(href_list["PRG_openfile"])) + error = "I/O error: Unable to open file '[href_list["PRG_openfile"]]'." + + if(href_list["PRG_newfile"]) + . = 1 + if(is_edited) + if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") + save_file(open_file) + + var/newname = sanitize(input(usr, "Enter file name:", "New File") as text|null) + if(!newname) + return 1 + var/datum/computer_file/data/F = create_file(newname) + if(F) + open_file = F.filename + loaded_data = "" + return 1 + else + error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'." + + if(href_list["PRG_saveasfile"]) + . = 1 + var/newname = sanitize(input(usr, "Enter file name:", "Save As") as text|null) + if(!newname) + return 1 + var/datum/computer_file/data/F = create_file(newname, loaded_data) + if(F) + open_file = F.filename + else + error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'." + return 1 + + if(href_list["PRG_savefile"]) + . = 1 + if(!open_file) + open_file = sanitize(input(usr, "Enter file name:", "Save As") as text|null) + if(!open_file) + return 0 + if(!save_file(open_file)) + error = "I/O error: Unable to save file '[open_file]'." + return 1 + + if(href_list["PRG_editfile"]) + var/oldtext = html_decode(loaded_data) + oldtext = replacetext(oldtext, "\[br\]", "\n") + + var/newtext = sanitize(replacetext(input(usr, "Editing file '[open_file]'. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) + if(!newtext) + return + loaded_data = newtext + is_edited = 1 + return 1 + + if(href_list["PRG_printfile"]) + . = 1 + if(!computer.nano_printer) + error = "Missing Hardware: Your computer does not have the required hardware to complete this operation." + return 1 + if(!computer.nano_printer.print_text(pencode2html(loaded_data))) + error = "Hardware error: Printer was unable to print the file. It may be out of paper." + return 1 + +/datum/nano_module/program/computer_wordprocessor + name = "Word Processor" + +/datum/nano_module/program/computer_wordprocessor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + var/datum/computer_file/program/wordprocessor/PRG + PRG = program + + var/obj/item/weapon/computer_hardware/hard_drive/HDD + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD + if(PRG.error) + data["error"] = PRG.error + if(PRG.browsing) + data["browsing"] = PRG.browsing + if(!PRG.computer || !PRG.computer.hard_drive) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + HDD = PRG.computer.hard_drive + var/list/files[0] + for(var/datum/computer_file/F in HDD.stored_files) + if(F.filetype == "TXT") + files.Add(list(list( + "name" = F.filename, + "size" = F.size + ))) + data["files"] = files + + RHDD = PRG.computer.portable_drive + if(RHDD) + data["usbconnected"] = 1 + var/list/usbfiles[0] + for(var/datum/computer_file/F in RHDD.stored_files) + if(F.filetype == "TXT") + usbfiles.Add(list(list( + "name" = F.filename, + "size" = F.size, + ))) + data["usbfiles"] = usbfiles + else if(PRG.open_file) + data["filedata"] = pencode2html(PRG.loaded_data) + data["filename"] = PRG.is_edited ? "[PRG.open_file]*" : PRG.open_file + else + data["filedata"] = pencode2html(PRG.loaded_data) + data["filename"] = "UNNAMED" + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "word_processor.tmpl", "Word Processor", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() \ No newline at end of file diff --git a/code/modules/nano/modules/crew_monitor.dm b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm similarity index 70% rename from code/modules/nano/modules/crew_monitor.dm rename to code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm index 319da90715..668d3f81e2 100644 --- a/code/modules/nano/modules/crew_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm @@ -1,52 +1,75 @@ -/datum/nano_module/crew_monitor - name = "Crew monitor" - -/datum/nano_module/crew_monitor/Topic(href, href_list) - if(..()) return 1 - var/turf/T = get_turf(nano_host()) // TODO: Allow setting any using_map.contact_levels from the interface. - if (!T || !(T.z in using_map.player_levels)) - usr << "Unable to establish a connection: You're too far away from the station!" - return 0 - if(href_list["track"]) - if(isAI(usr)) - var/mob/living/silicon/ai/AI = usr - var/mob/living/carbon/human/H = locate(href_list["track"]) in mob_list - if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) - AI.ai_actual_track(H) - return 1 - -/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/data[0] - var/turf/T = get_turf(nano_host()) - - data["isAI"] = isAI(user) - data["map_levels"] = using_map.get_map_levels(T.z, FALSE) - data["crewmembers"] = list() - for(var/z in (data["map_levels"] | T.z)) // Always show crew from the current Z even if we can't show a map - data["crewmembers"] += crew_repository.health_data(z) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800, state = state) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "crew_monitor_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") - if(!(ui.map_z_level in data["map_levels"])) - ui.set_map_z_level(data["map_levels"][1]) - - ui.set_initial_data(data) - ui.open() - - // should make the UI auto-update; doesn't seem to? - ui.set_auto_update(1) - -/*/datum/nano_module/crew_monitor/proc/scan() - for(var/mob/living/carbon/human/H in mob_list) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/C = H.w_uniform - if (C.has_sensor) - tracked |= C - return 1 +/datum/computer_file/program/suit_sensors + filename = "sensormonitor" + filedesc = "Suit Sensors Monitoring" + nanomodule_path = /datum/nano_module/crew_monitor + program_icon_state = "crew" + program_key_state = "med_key" + program_menu_icon = "heart" + extended_desc = "This program connects to life signs monitoring system to provide basic information on crew health." + required_access = access_medical + requires_ntnet = 1 + network_destination = "crew lifesigns monitoring system" + size = 11 + + + + + +/datum/nano_module/crew_monitor + name = "Crew monitor" + +/datum/nano_module/crew_monitor/Topic(href, href_list) + if(..()) return 1 + var/turf/T = get_turf(nano_host()) // TODO: Allow setting any using_map.contact_levels from the interface. + if (!T || !(T.z in using_map.player_levels)) + usr << "Unable to establish a connection: You're too far away from the station!" + return 0 + if(href_list["track"]) + if(isAI(usr)) + var/mob/living/silicon/ai/AI = usr + var/mob/living/carbon/human/H = locate(href_list["track"]) in mob_list + if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) + AI.ai_actual_track(H) + return 1 + +/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + var/turf/T = get_turf(nano_host()) + + data["isAI"] = isAI(user) + data["map_levels"] = using_map.get_map_levels(T.z, FALSE) + data["crewmembers"] = list() + for(var/z in data["map_levels"]) // VOREStation Edit + data["crewmembers"] += crew_repository.health_data(z) + + if(!data["map_levels"].len) + to_chat(user, "The crew monitor doesn't seem like it'll work here.") + if(ui) // VOREStation Addition + ui.close() // VOREStation Addition + return + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800, state = state) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "crew_monitor_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") + if(!(ui.map_z_level in data["map_levels"])) + ui.set_map_z_level(data["map_levels"][1]) + + ui.set_initial_data(data) + ui.open() + + // should make the UI auto-update; doesn't seem to? + ui.set_auto_update(1) + +/*/datum/nano_module/crew_monitor/proc/scan() + for(var/mob/living/carbon/human/H in mob_list) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/C = H.w_uniform + if (C.has_sensor) + tracked |= C + return 1 */ \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/research/email_administration.dm b/code/modules/modular_computers/file_system/programs/research/email_administration.dm new file mode 100644 index 0000000000..93ba0bded3 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/research/email_administration.dm @@ -0,0 +1,144 @@ +/datum/computer_file/program/email_administration + filename = "emailadmin" + filedesc = "Email Administration Utility" + extended_desc = "This program may be used to administrate NTNet's emailing service." + program_icon_state = "comm_monitor" + program_key_state = "generic_key" + program_menu_icon = "mail-open" + size = 12 + requires_ntnet = 1 + available_on_ntnet = 1 + nanomodule_path = /datum/nano_module/email_administration + required_access = access_network + + + + +/datum/nano_module/email_administration/ + name = "Email Client" + var/datum/computer_file/data/email_account/current_account = null + var/datum/computer_file/data/email_message/current_message = null + var/error = "" + +/datum/nano_module/email_administration/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + if(error) + data["error"] = error + else if(istype(current_message)) + data["msg_title"] = current_message.title + data["msg_body"] = pencode2html(current_message.stored_data) + data["msg_timestamp"] = current_message.timestamp + data["msg_source"] = current_message.source + else if(istype(current_account)) + data["current_account"] = current_account.login + data["cur_suspended"] = current_account.suspended + var/list/all_messages = list() + for(var/datum/computer_file/data/email_message/message in (current_account.inbox | current_account.spam | current_account.deleted)) + all_messages.Add(list(list( + "title" = message.title, + "source" = message.source, + "timestamp" = message.timestamp, + "uid" = message.uid + ))) + data["messages"] = all_messages + data["messagecount"] = all_messages.len + else + var/list/all_accounts = list() + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(!account.can_login) + continue + all_accounts.Add(list(list( + "login" = account.login, + "uid" = account.uid + ))) + data["accounts"] = all_accounts + data["accountcount"] = all_accounts.len + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "email_administration.tmpl", "Email Administration Utility", 600, 450, state = state) + if(host.update_layout()) + ui.auto_update_layout = 1 + ui.set_auto_update(1) + ui.set_initial_data(data) + ui.open() + + +/datum/nano_module/email_administration/Topic(href, href_list) + if(..()) + return 1 + + var/mob/user = usr + if(!istype(user)) + return 1 + + // High security - can only be operated when the user has an ID with access on them. + var/obj/item/weapon/card/id/I = user.GetIdCard() + if(!istype(I) || !(access_network in I.access)) + return 1 + + if(href_list["back"]) + if(error) + error = "" + else if(current_message) + current_message = null + else + current_account = null + return 1 + + if(href_list["ban"]) + if(!current_account) + return 1 + + current_account.suspended = !current_account.suspended + ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended by SA [I.registered_name] ([I.assignment]).") + error = "Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended." + return 1 + + if(href_list["changepass"]) + if(!current_account) + return 1 + + var/newpass = sanitize(input(user,"Enter new password for account [current_account.login]", "Password"), 100) + if(!newpass) + return 1 + current_account.password = newpass + ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Password for account [current_account.login] has been changed by SA [I.registered_name] ([I.assignment]).") + return 1 + + if(href_list["viewmail"]) + if(!current_account) + return 1 + + for(var/datum/computer_file/data/email_message/received_message in (current_account.inbox | current_account.spam | current_account.deleted)) + if(received_message.uid == text2num(href_list["viewmail"])) + current_message = received_message + break + return 1 + + if(href_list["viewaccount"]) + for(var/datum/computer_file/data/email_account/email_account in ntnet_global.email_accounts) + if(email_account.uid == text2num(href_list["viewaccount"])) + current_account = email_account + break + return 1 + + if(href_list["newaccount"]) + var/newdomain = sanitize(input(user,"Pick domain:", "Domain name") as null|anything in using_map.usable_email_tlds) + if(!newdomain) + return 1 + var/newlogin = sanitize(input(user,"Pick account name (@[newdomain]):", "Account name"), 100) + if(!newlogin) + return 1 + + var/complete_login = "[newlogin]@[newdomain]" + if(ntnet_global.does_email_exist(complete_login)) + error = "Error creating account: An account with same address already exists." + return 1 + + var/datum/computer_file/data/email_account/new_account = new/datum/computer_file/data/email_account() + new_account.login = complete_login + new_account.password = GenerateKey() + error = "Email [new_account.login] has been created, with generated password [new_account.password]" + return 1 diff --git a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm new file mode 100644 index 0000000000..590837f2b5 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm @@ -0,0 +1,102 @@ +/datum/computer_file/program/ntnetmonitor + filename = "ntmonitor" + filedesc = "NTNet Diagnostics and Monitoring" + program_icon_state = "comm_monitor" + program_key_state = "generic_key" + program_menu_icon = "wrench" + extended_desc = "This program monitors the local NTNet network, provides access to logging systems, and allows for configuration changes" + size = 12 + requires_ntnet = 1 + required_access = access_network + available_on_ntnet = 1 + nanomodule_path = /datum/nano_module/computer_ntnetmonitor/ + +/datum/nano_module/computer_ntnetmonitor + name = "NTNet Diagnostics and Monitoring" + //available_to_ai = TRUE + +/datum/nano_module/computer_ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!ntnet_global) + return + var/list/data = host.initial_data() + + data["ntnetstatus"] = ntnet_global.check_function() + data["ntnetrelays"] = ntnet_global.relays.len + data["idsstatus"] = ntnet_global.intrusion_detection_enabled + data["idsalarm"] = ntnet_global.intrusion_detection_alarm + + data["config_softwaredownload"] = ntnet_global.setting_softwaredownload + data["config_peertopeer"] = ntnet_global.setting_peertopeer + data["config_communication"] = ntnet_global.setting_communication + data["config_systemcontrol"] = ntnet_global.setting_systemcontrol + + data["ntnetlogs"] = ntnet_global.logs + data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount + + data["banned_nids"] = list(ntnet_global.banned_nids) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700, state = state) + if(host.update_layout()) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/nano_module/computer_ntnetmonitor/Topic(href, href_list, state) + var/mob/user = usr + if(..()) + return 1 + if(href_list["resetIDS"]) + if(ntnet_global) + ntnet_global.resetIDS() + return 1 + if(href_list["toggleIDS"]) + if(ntnet_global) + ntnet_global.toggleIDS() + return 1 + if(href_list["toggleWireless"]) + if(!ntnet_global) + return 1 + + // NTNet is disabled. Enabling can be done without user prompt + if(ntnet_global.setting_disabled) + ntnet_global.setting_disabled = 0 + return 1 + + // NTNet is enabled and user is about to shut it down. Let's ask them if they really want to do it, as wirelessly connected computers won't connect without NTNet being enabled (which may prevent people from turning it back on) + if(!user) + return 1 + var/response = alert(user, "Really disable NTNet wireless? If your computer is connected wirelessly you won't be able to turn it back on! This will affect all connected wireless devices.", "NTNet shutdown", "Yes", "No") + if(response == "Yes") + ntnet_global.setting_disabled = 1 + return 1 + if(href_list["purgelogs"]) + if(ntnet_global) + ntnet_global.purge_logs() + return 1 + if(href_list["updatemaxlogs"]) + var/logcount = text2num(input(user,"Enter amount of logs to keep in memory ([MIN_NTNET_LOGS]-[MAX_NTNET_LOGS]):")) + if(ntnet_global) + ntnet_global.update_max_log_count(logcount) + return 1 + if(href_list["toggle_function"]) + if(!ntnet_global) + return 1 + ntnet_global.toggle_function(href_list["toggle_function"]) + return 1 + if(href_list["ban_nid"]) + if(!ntnet_global) + return 1 + var/nid = input(user,"Enter NID of device which you want to block from the network:", "Enter NID") as null|num + if(nid && CanUseTopic(user, state)) + ntnet_global.banned_nids |= nid + return 1 + if(href_list["unban_nid"]) + if(!ntnet_global) + return 1 + var/nid = input(user,"Enter NID of device which you want to unblock from the network:", "Enter NID") as null|num + if(nid && CanUseTopic(user, state)) + ntnet_global.banned_nids -= nid + return 1 diff --git a/code/modules/modular_computers/file_system/programs/security/alarm_monitor.dm b/code/modules/modular_computers/file_system/programs/security/alarm_monitor.dm new file mode 100644 index 0000000000..2254cc1dae --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/security/alarm_monitor.dm @@ -0,0 +1,6 @@ +/datum/computer_file/program/alarm_monitor/security //Subtype of engineering alarm monitor, look at /engineering/alarm_monitor for details + filename = "alarmmonitorsec" + filedesc = "Alarm Monitoring (Security)" + extended_desc = "This program provides visual interface for the security alarm system." + nanomodule_path = /datum/nano_module/alarm_monitor/security + required_access = access_security \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm new file mode 100644 index 0000000000..28ca1b73f5 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm @@ -0,0 +1,143 @@ +var/warrant_uid = 0 +/datum/datacore/var/list/warrants[] = list() +/datum/data/record/warrant + var/warrant_id + +/datum/data/record/warrant/New() + ..() + warrant_id = warrant_uid++ + + +/datum/computer_file/program/digitalwarrant + filename = "digitalwarrant" + filedesc = "Warrant Assistant" + extended_desc = "Official NTsec program for creation and handling of warrants." + size = 8 + program_icon_state = "warrant" + program_key_state = "security_key" + program_menu_icon = "star" + requires_ntnet = 1 + available_on_ntnet = 1 + required_access = access_security + usage_flags = PROGRAM_ALL + nanomodule_path = /datum/nano_module/program/digitalwarrant/ + +/datum/nano_module/program/digitalwarrant/ + name = "Warrant Assistant" + var/datum/data/record/warrant/activewarrant + +/datum/nano_module/program/digitalwarrant/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/list/data = host.initial_data() + + if(activewarrant) + data["warrantname"] = activewarrant.fields["namewarrant"] + data["warrantcharges"] = activewarrant.fields["charges"] + data["warrantauth"] = activewarrant.fields["auth"] + data["type"] = activewarrant.fields["arrestsearch"] + else + var/list/allwarrants = list() + for(var/datum/data/record/warrant/W in data_core.warrants) + allwarrants.Add(list(list( + "warrantname" = W.fields["namewarrant"], + "charges" = "[copytext(W.fields["charges"],1,min(length(W.fields["charges"]) + 1, 50))]...", + "auth" = W.fields["auth"], + "id" = W.warrant_id, + "arrestsearch" = W.fields["arrestsearch"] + ))) + data["allwarrants"] = allwarrants + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "digitalwarrant.tmpl", name, 500, 350, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + +/datum/nano_module/program/digitalwarrant/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["sw_menu"]) + activewarrant = null + + if(href_list["editwarrant"]) + . = 1 + for(var/datum/data/record/warrant/W in data_core.warrants) + if(W.warrant_id == text2num(href_list["editwarrant"])) + activewarrant = W + break + + // The following actions will only be possible if the user has an ID with security access equipped. This is in line with modular computer framework's authentication methods, + // which also use RFID scanning to allow or disallow access to some functions. Anyone can view warrants, editing requires ID. This also prevents situations where you show a tablet + // to someone who is to be arrested, which allows them to change the stuff there. + + var/mob/user = usr + if(!istype(user)) + return + var/obj/item/weapon/card/id/I = user.GetIdCard() + if(!istype(I) || !I.registered_name || !(access_security in I.access)) + to_chat(user, "Authentication error: Unable to locate ID with apropriate access to allow this operation.") + return + + if(href_list["addwarrant"]) + . = 1 + var/datum/data/record/warrant/W = new() + var/temp = sanitize(input(usr, "Do you want to create a search-, or an arrest warrant?") as null|anything in list("search","arrest")) + if(CanInteract(user, default_state)) + if(temp == "arrest") + W.fields["namewarrant"] = "Unknown" + W.fields["charges"] = "No charges present" + W.fields["auth"] = "Unauthorized" + W.fields["arrestsearch"] = "arrest" + if(temp == "search") + W.fields["namewarrant"] = "No location given" + W.fields["charges"] = "No reason given" + W.fields["auth"] = "Unauthorized" + W.fields["arrestsearch"] = "search" + activewarrant = W + + if(href_list["savewarrant"]) + . = 1 + data_core.warrants |= activewarrant + activewarrant = null + + if(href_list["deletewarrant"]) + . = 1 + data_core.warrants -= activewarrant + activewarrant = null + + if(href_list["editwarrantname"]) + . = 1 + var/namelist = list() + for(var/datum/data/record/t in data_core.general) + namelist += t.fields["name"] + var/new_name = sanitize(input(usr, "Please input name") as null|anything in namelist) + if(CanInteract(user, default_state)) + if (!new_name) + return + activewarrant.fields["namewarrant"] = new_name + + if(href_list["editwarrantnamecustom"]) + . = 1 + var/new_name = sanitize(input("Please input name") as null|text) + if(CanInteract(user, default_state)) + if (!new_name) + return + activewarrant.fields["namewarrant"] = new_name + + if(href_list["editwarrantcharges"]) + . = 1 + var/new_charges = sanitize(input("Please input charges", "Charges", activewarrant.fields["charges"]) as null|text) + if(CanInteract(user, default_state)) + if (!new_charges) + return + activewarrant.fields["charges"] = new_charges + + if(href_list["editwarrantauth"]) + . = 1 + + activewarrant.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]" + + if(href_list["back"]) + . = 1 + activewarrant = null diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm new file mode 100644 index 0000000000..fb73609d84 --- /dev/null +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -0,0 +1,87 @@ +/obj/item/weapon/computer_hardware/ + name = "Hardware" + desc = "Unknown Hardware." + icon = 'icons/obj/modular_components.dmi' + var/obj/item/modular_computer/holder2 = null + var/power_usage = 0 // If the hardware uses extra power, change this. + var/enabled = 1 // If the hardware is turned off set this to 0. + var/critical = 1 // Prevent disabling for important component, like the HDD. + var/hardware_size = 1 // Limits which devices can contain this component. 1: Tablets/Laptops/Consoles, 2: Laptops/Consoles, 3: Consoles only + var/damage = 0 // Current damage level + var/max_damage = 100 // Maximal damage level. + var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things + var/damage_failure = 50 // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all. + var/malfunction_probability = 10// Chance of malfunction when the component is damaged + +/obj/item/weapon/computer_hardware/attackby(var/obj/item/W as obj, var/mob/living/user as mob) + // Multitool. Runs diagnostics + if(istype(W, /obj/item/device/multitool)) + to_chat(user, "***** DIAGNOSTICS REPORT *****") + diagnostics(user) + to_chat(user, "******************************") + return 1 + // Nanopaste. Repair all damage if present for a single unit. + var/obj/item/stack/S = W + if(istype(S, /obj/item/stack/nanopaste)) + if(!damage) + to_chat(user, "\The [src] doesn't seem to require repairs.") + return 1 + if(S.use(1)) + to_chat(user, "You apply a bit of \the [W] to \the [src]. It immediately repairs all damage.") + damage = 0 + return 1 + // Cable coil. Works as repair method, but will probably require multiple applications and more cable. + if(istype(S, /obj/item/stack/cable_coil)) + if(!damage) + to_chat(user, "\The [src] doesn't seem to require repairs.") + return 1 + if(S.use(1)) + to_chat(user, "You patch up \the [src] with a bit of \the [W].") + take_damage(-10) + return 1 + return ..() + + +// Called on multitool click, prints diagnostic information to the user. +/obj/item/weapon/computer_hardware/proc/diagnostics(var/mob/user) + to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]") + +/obj/item/weapon/computer_hardware/New(var/obj/L) + w_class = hardware_size + if(istype(L, /obj/item/modular_computer)) + holder2 = L + return + +/obj/item/weapon/computer_hardware/Destroy() + holder2 = null + return ..() + +// Handles damage checks +/obj/item/weapon/computer_hardware/proc/check_functionality() + // Turned off + if(!enabled) + return 0 + // Too damaged to work at all. + if(damage > damage_failure) + return 0 + // Still working. Well, sometimes... + if(damage > damage_malfunction) + if(prob(malfunction_probability)) + return 0 + // Good to go. + return 1 + +/obj/item/weapon/computer_hardware/examine(var/mob/user) + . = ..() + if(damage > damage_failure) + to_chat(user, "It seems to be severely damaged!") + else if(damage > damage_malfunction) + to_chat(user, "It seems to be damaged!") + else if(damage) + to_chat(user, "It seems to be slightly damaged.") + +// Damages the component. Contains necessary checks. Negative damage "heals" the component. +/obj/item/weapon/computer_hardware/take_damage(var/amount) + damage += round(amount) // We want nice rounded numbers here. + damage = between(0, damage, max_damage) // Clamp the value. + diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm new file mode 100644 index 0000000000..3b7ea1164b --- /dev/null +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -0,0 +1,79 @@ +// This device is wrapper for actual power cell. I have decided to not use power cells directly as even low-end cells available on station +// have tremendeous capacity in comparsion. Higher tier cells would provide your device with nearly infinite battery life, which is something i want to avoid. +/obj/item/weapon/computer_hardware/battery_module + name = "standard battery" + desc = "A standard power cell, commonly seen in high-end portable microcomputers or low-end laptops. It's rating is 750." + icon_state = "battery_normal" + critical = 1 + malfunction_probability = 1 + origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 1) + var/battery_rating = 750 + var/obj/item/weapon/cell/battery = null + +/obj/item/weapon/computer_hardware/battery_module/advanced + name = "advanced battery" + desc = "An advanced power cell, often used in most laptops. It is too large to be fitted into smaller devices. It's rating is 1100." + icon_state = "battery_advanced" + origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2) + hardware_size = 2 + battery_rating = 1100 + +/obj/item/weapon/computer_hardware/battery_module/super + name = "super battery" + desc = "A very advanced power cell, often used in high-end devices, or as uninterruptable power supply for important consoles or servers. It's rating is 1500." + icon_state = "battery_super" + origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3) + hardware_size = 2 + battery_rating = 1500 + +/obj/item/weapon/computer_hardware/battery_module/ultra + name = "ultra battery" + desc = "A very advanced large power cell. It's often used as uninterruptable power supply for critical consoles or servers. It's rating is 2000." + icon_state = "battery_ultra" + origin_tech = list(TECH_POWER = 5, TECH_ENGINEERING = 4) + hardware_size = 3 + battery_rating = 2000 + +/obj/item/weapon/computer_hardware/battery_module/micro + name = "micro battery" + desc = "A small power cell, commonly seen in most portable microcomputers. It's rating is 500." + icon_state = "battery_micro" + origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2) + battery_rating = 500 + +/obj/item/weapon/computer_hardware/battery_module/nano + name = "nano battery" + desc = "A tiny power cell, commonly seen in low-end portable microcomputers. It's rating is 300." + icon_state = "battery_nano" + origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 1) + battery_rating = 300 + +// This is not intended to be obtainable in-game. Intended for adminbus and debugging purposes. +/obj/item/weapon/computer_hardware/battery_module/lambda + name = "lambda coil" + desc = "A very complex power source compatible with various computers. It is capable of providing power for nearly unlimited duration." + icon_state = "battery_lambda" + hardware_size = 1 + battery_rating = 30000 + +/obj/item/weapon/computer_hardware/battery_module/lambda/New() + ..() + battery = new/obj/item/weapon/cell/infinite(src) + +/obj/item/weapon/computer_hardware/battery_module/diagnostics(var/mob/user) + ..() + to_chat(user, "Internal battery charge: [battery.charge]/[battery.maxcharge] CU") + +/obj/item/weapon/computer_hardware/battery_module/New() + battery = new/obj/item/weapon/cell(src) + battery.maxcharge = battery_rating + battery.charge = 0 + ..() + +/obj/item/weapon/computer_hardware/battery_module/Destroy() + qdel_null(battery) + return ..() + +/obj/item/weapon/computer_hardware/battery_module/proc/charge_to_full() + if(battery) + battery.charge = battery.maxcharge \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm new file mode 100644 index 0000000000..4cf8afc705 --- /dev/null +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -0,0 +1,18 @@ +/obj/item/weapon/computer_hardware/card_slot + name = "RFID card slot" + desc = "Slot that allows this computer to write data on RFID cards. Necessary for some programs to run properly." + power_usage = 10 //W + critical = 0 + icon_state = "cardreader" + hardware_size = 1 + origin_tech = list(TECH_DATA = 2) + + var/obj/item/weapon/card/id/stored_card = null + +/obj/item/weapon/computer_hardware/card_slot/Destroy() + if(holder2 && (holder2.card_slot == src)) + holder2.card_slot = null + if(stored_card) + stored_card.forceMove(get_turf(holder2)) + holder2 = null + return ..() \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm new file mode 100644 index 0000000000..c9aa58d8d1 --- /dev/null +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -0,0 +1,167 @@ +/obj/item/weapon/computer_hardware/hard_drive/ + name = "basic hard drive" + desc = "A small power efficient solid state drive, with 128GQ of storage capacity for use in basic computers where power efficiency is desired." + power_usage = 25 // SSD or something with low power usage + icon_state = "hdd_normal" + hardware_size = 1 + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + var/max_capacity = 128 + var/used_capacity = 0 + var/list/stored_files = list() // List of stored files on this drive. DO NOT MODIFY DIRECTLY! + +/obj/item/weapon/computer_hardware/hard_drive/advanced + name = "advanced hard drive" + desc = "A small hybrid hard drive with 256GQ of storage capacity for use in higher grade computers where balance between power efficiency and capacity is desired." + max_capacity = 256 + origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + power_usage = 50 // Hybrid, medium capacity and medium power storage + icon_state = "hdd_advanced" + hardware_size = 2 + +/obj/item/weapon/computer_hardware/hard_drive/super + name = "super hard drive" + desc = "A small hard drive with 512GQ of storage capacity for use in cluster storage solutions where capacity is more important than power efficiency." + max_capacity = 512 + origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) + power_usage = 100 // High-capacity but uses lots of power, shortening battery life. Best used with APC link. + icon_state = "hdd_super" + hardware_size = 2 + +/obj/item/weapon/computer_hardware/hard_drive/cluster + name = "cluster hard drive" + desc = "A large storage cluster consisting of multiple hard drives for usage in high capacity storage systems. Has capacity of 2048 GQ." + power_usage = 500 + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4) + max_capacity = 2048 + icon_state = "hdd_cluster" + hardware_size = 3 + +// For tablets, etc. - highly power efficient. +/obj/item/weapon/computer_hardware/hard_drive/small + name = "small hard drive" + desc = "A small highly efficient solid state drive for portable devices." + power_usage = 10 + origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + max_capacity = 64 + icon_state = "hdd_small" + hardware_size = 1 + +/obj/item/weapon/computer_hardware/hard_drive/micro + name = "micro hard drive" + desc = "A small micro hard drive for portable devices." + power_usage = 2 + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + max_capacity = 32 + icon_state = "hdd_micro" + hardware_size = 1 + +/obj/item/weapon/computer_hardware/hard_drive/diagnostics(var/mob/user) + ..() + // 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space. + to_chat(user, "NT-NFS File Table Status: [stored_files.len]/999") + to_chat(user, "Storage capacity: [used_capacity]/[max_capacity]GQ") + +// Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. +/obj/item/weapon/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F) + if(!F || !istype(F)) + return 0 + + if(!can_store_file(F.size)) + return 0 + + if(!check_functionality()) + return 0 + + if(!stored_files) + return 0 + + // This file is already stored. Don't store it again. + if(F in stored_files) + return 0 + + F.holder = src + stored_files.Add(F) + recalculate_size() + return 1 + +// Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. +/obj/item/weapon/computer_hardware/hard_drive/proc/install_default_programs() + store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar + store_file(new/datum/computer_file/program/ntnetdownload(src)) // NTNet Downloader Utility, allows users to download more software from NTNet repository + store_file(new/datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. + + +// Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. +/obj/item/weapon/computer_hardware/hard_drive/proc/remove_file(var/datum/computer_file/F) + if(!F || !istype(F)) + return 0 + + if(!stored_files) + return 0 + + if(!check_functionality()) + return 0 + + if(F in stored_files) + stored_files -= F + recalculate_size() + return 1 + else + return 0 + +// Loops through all stored files and recalculates used_capacity of this drive +/obj/item/weapon/computer_hardware/hard_drive/proc/recalculate_size() + var/total_size = 0 + for(var/datum/computer_file/F in stored_files) + total_size += F.size + + used_capacity = total_size + +// Checks whether file can be stored on the hard drive. +/obj/item/weapon/computer_hardware/hard_drive/proc/can_store_file(var/size = 1) + // In the unlikely event someone manages to create that many files. + // BYOND is acting weird with numbers above 999 in loops (infinite loop prevention) + if(stored_files.len >= 999) + return 0 + if(used_capacity + size > max_capacity) + return 0 + else + return 1 + +// Checks whether we can store the file. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file. +/obj/item/weapon/computer_hardware/hard_drive/proc/try_store_file(var/datum/computer_file/F) + if(!F || !istype(F)) + return 0 + var/name = F.filename + "." + F.filetype + for(var/datum/computer_file/file in stored_files) + if((file.filename + "." + file.filetype) == name) + return 0 + return can_store_file(F.size) + + + +// Tries to find the file by filename. Returns null on failure +/obj/item/weapon/computer_hardware/hard_drive/proc/find_file_by_name(var/filename) + if(!check_functionality()) + return null + + if(!filename) + return null + + if(!stored_files) + return null + + for(var/datum/computer_file/F in stored_files) + if(F.filename == filename) + return F + return null + +/obj/item/weapon/computer_hardware/hard_drive/Destroy() + if(holder2 && (holder2.hard_drive == src)) + holder2.hard_drive = null + stored_files = null + return ..() + +/obj/item/weapon/computer_hardware/hard_drive/New() + install_default_programs() + ..() \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/nano_printer.dm b/code/modules/modular_computers/hardware/nano_printer.dm new file mode 100644 index 0000000000..665b855f4f --- /dev/null +++ b/code/modules/modular_computers/hardware/nano_printer.dm @@ -0,0 +1,92 @@ +/obj/item/weapon/computer_hardware/nano_printer + name = "nano printer" + desc = "Small integrated printer with paper recycling module." + power_usage = 50 + origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + critical = 0 + icon_state = "printer" + hardware_size = 1 + var/stored_paper = 5 + var/max_paper = 10 + +/obj/item/weapon/computer_hardware/nano_printer/diagnostics(var/mob/user) + ..() + to_chat(user, "Paper buffer level: [stored_paper]/[max_paper]") + +/obj/item/weapon/computer_hardware/nano_printer/proc/print_text(var/text_to_print, var/paper_title = null) + if(!stored_paper) + return 0 + if(!enabled) + return 0 + if(!check_functionality()) + return 0 + + var/obj/item/weapon/paper/P = new/obj/item/weapon/paper(get_turf(holder2)) + + // Damaged printer causes the resulting paper to be somewhat harder to read. + if(damage > damage_malfunction) + P.info = stars(text_to_print, 100-malfunction_probability) + else + P.info = text_to_print + if(paper_title) + P.name = paper_title + P.update_icon() + P.fields = count_fields(P.info) + P.updateinfolinks() + + stored_paper-- + return 1 + +/obj/item/weapon/computer_hardware/nano_printer/proc/count_fields(var/info) +//Count the fields. This is taken directly from paper.dm, /obj/item/weapon/paper/proc/parsepencode(). -Hawk_v3 + var/fields = 0 + var/t = info + var/laststart = 1 + while(1) + var/i = findtext(t, "", laststart) // + if(i==0) + break + laststart = i+1 + fields++ + return fields + +/obj/item/weapon/computer_hardware/nano_printer/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/paper)) + if(stored_paper >= max_paper) + to_chat(user, "You try to add \the [W] into \the [src], but its paper bin is full.") + return + + to_chat(user, "You insert \the [W] into [src].") + qdel(W) + stored_paper++ + else if(istype(W, /obj/item/weapon/paper_bundle)) + var/obj/item/weapon/paper_bundle/B = W + var/num_of_pages_added = 0 + if(stored_paper >= max_paper) + to_chat(user, "You try to add \the [W] into \the [src], but its paper bin is full.") + return + for(var/obj/item/weapon/bundleitem in B) //loop through items in bundle + if(istype(bundleitem, /obj/item/weapon/paper)) //if item is paper (and not photo), add into the bin + B.pages.Remove(bundleitem) + qdel(bundleitem) + num_of_pages_added++ + stored_paper++ + if(stored_paper >= max_paper) //check if the printer is full yet + to_chat(user, "The printer has been filled to full capacity.") + break + if(B.pages.len == 0) //if all its papers have been put into the printer, delete bundle + qdel(W) + else if(B.pages.len == 1) //if only one item left, extract item and delete the one-item bundle + user.drop_from_inventory(B) + user.put_in_hands(B[1]) + qdel(B) + else //if at least two items remain, just update the bundle icon + B.update_icon() + to_chat(user, "You add [num_of_pages_added] papers from \the [W] into \the [src].") + return + +/obj/item/weapon/computer_hardware/nano_printer/Destroy() + if(holder2 && (holder2.nano_printer == src)) + holder2.nano_printer = null + holder2 = null + return ..() \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm new file mode 100644 index 0000000000..ecf0bab6f6 --- /dev/null +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -0,0 +1,100 @@ +var/global/ntnet_card_uid = 1 + +/obj/item/weapon/computer_hardware/network_card/ + name = "basic NTNet network card" + desc = "A basic network card for usage with standard NTNet frequencies." + power_usage = 50 + origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 1) + critical = 0 + icon_state = "netcard_basic" + hardware_size = 1 + var/identification_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user. + var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user. + var/long_range = 0 + var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks. + malfunction_probability = 1 + +/obj/item/weapon/computer_hardware/network_card/diagnostics(var/mob/user) + ..() + to_chat(user, "NIX Unique ID: [identification_id]") + to_chat(user, "NIX User Tag: [identification_string]") + to_chat(user, "Supported protocols:") + to_chat(user, "511.m SFS (Subspace) - Standard Frequency Spread") + if(long_range) + to_chat(user, "511.n WFS/HB (Subspace) - Wide Frequency Spread/High Bandiwdth") + if(ethernet) + to_chat(user, "OpenEth (Physical Connection) - Physical network connection port") + +/obj/item/weapon/computer_hardware/network_card/New(var/l) + ..(l) + identification_id = ntnet_card_uid + ntnet_card_uid++ + +/obj/item/weapon/computer_hardware/network_card/advanced + name = "advanced NTNet network card" + desc = "An advanced network card for usage with standard NTNet frequencies. It's transmitter is strong enough to connect even when far away." + long_range = 1 + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 2) + power_usage = 100 // Better range but higher power usage. + icon_state = "netcard_advanced" + hardware_size = 1 + +/obj/item/weapon/computer_hardware/network_card/wired + name = "wired NTNet network card" + desc = "An advanced network card for usage with standard NTNet frequencies. This one also supports wired connection." + ethernet = 1 + origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 3) + power_usage = 100 // Better range but higher power usage. + icon_state = "netcard_ethernet" + hardware_size = 3 + +/obj/item/weapon/computer_hardware/network_card/Destroy() + if(holder2 && (holder2.network_card == src)) + holder2.network_card = null + holder2 = null + return ..() + +// Returns a string identifier of this network card +/obj/item/weapon/computer_hardware/network_card/proc/get_network_tag() + return "[identification_string] (NID [identification_id])" + +/obj/item/weapon/computer_hardware/network_card/proc/is_banned() + return ntnet_global.check_banned(identification_id) + +// 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection +/obj/item/weapon/computer_hardware/network_card/proc/get_signal(var/specific_action = 0) + if(!holder2) // Hardware is not installed in anything. No signal. How did this even get called? + return 0 + + if(!enabled) + return 0 + + if(!check_functionality() || !ntnet_global || is_banned()) + return 0 + + if(ethernet) // Computer is connected via wired connection. + return 3 + + if(!ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. + return 0 + + if(holder2) + var/turf/T = get_turf(holder2) + if(!istype(T)) //no reception in nullspace + return 0 + if(T.z in using_map.station_levels) + // Computer is on station. Low/High signal depending on what type of network card you have + if(long_range) + return 2 + else + return 1 + if(T.z in using_map.contact_levels) //not on station, but close enough for radio signal to travel + if(long_range) // Computer is not on station, but it has upgraded network card. Low signal. + return 1 + + return 0 // Computer is not on station and does not have upgraded network card. No signal. + +/obj/item/weapon/computer_hardware/network_card/Destroy() + if(holder2 && (holder2.network_card == src)) + holder2.network_card = null + ..() diff --git a/code/modules/modular_computers/hardware/portable_hard_drive.dm b/code/modules/modular_computers/hardware/portable_hard_drive.dm new file mode 100644 index 0000000000..817fca789e --- /dev/null +++ b/code/modules/modular_computers/hardware/portable_hard_drive.dm @@ -0,0 +1,37 @@ +// These are basically USB data sticks and may be used to transfer files between devices +/obj/item/weapon/computer_hardware/hard_drive/portable + name = "basic data crystal" + desc = "Small crystal with imprinted photonic circuits that can be used to store data. Its capacity is 16 GQ." + power_usage = 10 + icon_state = "flashdrive_basic" + hardware_size = 1 + max_capacity = 16 + origin_tech = list(TECH_DATA = 1) + +/obj/item/weapon/computer_hardware/hard_drive/portable/advanced + name = "advanced data crystal" + desc = "Small crystal with imprinted high-density photonic circuits that can be used to store data. Its capacity is 64 GQ." + power_usage = 20 + icon_state = "flashdrive_advanced" + hardware_size = 1 + max_capacity = 64 + origin_tech = list(TECH_DATA = 2) + +/obj/item/weapon/computer_hardware/hard_drive/portable/super + name = "super data crystal" + desc = "Small crystal with imprinted ultra-density photonic circuits that can be used to store data. Its capacity is 256 GQ." + power_usage = 40 + icon_state = "flashdrive_super" + hardware_size = 1 + max_capacity = 256 + origin_tech = list(TECH_DATA = 4) + +/obj/item/weapon/computer_hardware/hard_drive/portable/New() + ..() + stored_files = list() + recalculate_size() + +/obj/item/weapon/computer_hardware/hard_drive/portable/Destroy() + if(holder2 && (holder2.portable_drive == src)) + holder2.portable_drive = null + return ..() \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/processor_unit.dm b/code/modules/modular_computers/hardware/processor_unit.dm new file mode 100644 index 0000000000..9ed7ab40ac --- /dev/null +++ b/code/modules/modular_computers/hardware/processor_unit.dm @@ -0,0 +1,46 @@ +// CPU that allows the computer to run programs. +// Better CPUs are obtainable via research and can run more programs on background. + +/obj/item/weapon/computer_hardware/processor_unit + name = "standard processor" + desc = "A standard CPU used in most computers. It can run up to three programs simultaneously." + icon_state = "cpu_normal" + hardware_size = 2 + power_usage = 50 + critical = 1 + malfunction_probability = 1 + origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 2) + + var/max_idle_programs = 2 // 2 idle, + 1 active = 3 as said in description. + +/obj/item/weapon/computer_hardware/processor_unit/small + name = "standard microprocessor" + desc = "A standard miniaturised CPU used in portable devices. It can run up to two programs simultaneously." + icon_state = "cpu_small" + hardware_size = 1 + power_usage = 25 + max_idle_programs = 1 + origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + +/obj/item/weapon/computer_hardware/processor_unit/photonic + name = "photonic processor" + desc = "An advanced experimental CPU that uses photonic core instead of regular circuitry. It can run up to five programs simultaneously, but uses a lot of power." + icon_state = "cpu_normal_photonic" + hardware_size = 2 + power_usage = 250 + max_idle_programs = 4 + origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 4) + +/obj/item/weapon/computer_hardware/processor_unit/photonic/small + name = "photonic microprocessor" + desc = "An advanced miniaturised CPU for use in portable devices. It uses photonic core instead of regular circuitry. It can run up to three programs simultaneously." + icon_state = "cpu_small_photonic" + hardware_size = 1 + power_usage = 75 + max_idle_programs = 2 + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3) + +/obj/item/weapon/computer_hardware/processor_unit/Destroy() + if(holder2 && (holder2.processor_unit == src)) + holder2.processor_unit = null + return ..() \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/tesla_link.dm b/code/modules/modular_computers/hardware/tesla_link.dm new file mode 100644 index 0000000000..b23bb963af --- /dev/null +++ b/code/modules/modular_computers/hardware/tesla_link.dm @@ -0,0 +1,14 @@ +/obj/item/weapon/computer_hardware/tesla_link + name = "tesla link" + desc = "An advanced tesla link that wirelessly recharges connected device from nearby area power controller." + critical = 0 + enabled = 1 + icon_state = "teslalink" + hardware_size = 1 + origin_tech = list(TECH_DATA = 2, TECH_POWER = 3, TECH_ENGINEERING = 2) + var/passive_charging_rate = 250 // W + +/obj/item/weapon/computer_hardware/tesla_link/Destroy() + if(holder2 && (holder2.tesla_link == src)) + holder2.tesla_link = null + return ..() \ No newline at end of file diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm new file mode 100644 index 0000000000..ab60291f55 --- /dev/null +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -0,0 +1,308 @@ +// A vendor machine for modular computer portable devices - Laptops and Tablets + +/obj/machinery/lapvend + name = "computer vendor" + desc = "A vending machine with a built-in microfabricator, capable of dispensing various NT-branded computers." + icon = 'icons/obj/vending.dmi' + icon_state = "robotics" + layer = OBJ_LAYER - 0.1 + anchored = 1 + density = 1 + + // The actual laptop/tablet + var/obj/item/modular_computer/laptop/fabricated_laptop = null + var/obj/item/modular_computer/tablet/fabricated_tablet = null + + // Utility vars + var/state = 0 // 0: Select device type, 1: Select loadout, 2: Payment, 3: Thankyou screen + var/devtype = 0 // 0: None(unselected), 1: Laptop, 2: Tablet + var/total_price = 0 // Price of currently vended device. + + // Device loadout + var/dev_cpu = 1 // 1: Default, 2: Upgraded + var/dev_battery = 1 // 1: Default, 2: Upgraded, 3: Advanced + var/dev_disk = 1 // 1: Default, 2: Upgraded, 3: Advanced + var/dev_netcard = 0 // 0: None, 1: Basic, 2: Long-Range + var/dev_tesla = 0 // 0: None, 1: Standard + var/dev_nanoprint = 0 // 0: None, 1: Standard + var/dev_card = 0 // 0: None, 1: Standard + +// Removes all traces of old order and allows you to begin configuration from scratch. +/obj/machinery/lapvend/proc/reset_order() + state = 0 + devtype = 0 + if(fabricated_laptop) + qdel(fabricated_laptop) + fabricated_laptop = null + if(fabricated_tablet) + qdel(fabricated_tablet) + fabricated_tablet = null + dev_cpu = 1 + dev_battery = 1 + dev_disk = 1 + dev_netcard = 0 + dev_tesla = 0 + dev_nanoprint = 0 + dev_card = 0 + +// Recalculates the price and optionally even fabricates the device. +/obj/machinery/lapvend/proc/fabricate_and_recalc_price(var/fabricate = 0) + total_price = 0 + if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles + if(fabricate) + fabricated_laptop = new(src) + total_price = 99 + switch(dev_cpu) + if(1) + if(fabricate) + fabricated_laptop.processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(fabricated_laptop) + if(2) + if(fabricate) + fabricated_laptop.processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(fabricated_laptop) + total_price += 299 + switch(dev_battery) + if(1) // Basic(750C) + if(fabricate) + fabricated_laptop.battery_module = new/obj/item/weapon/computer_hardware/battery_module(fabricated_laptop) + if(2) // Upgraded(1100C) + if(fabricate) + fabricated_laptop.battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(fabricated_laptop) + total_price += 199 + if(3) // Advanced(1500C) + if(fabricate) + fabricated_laptop.battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(fabricated_laptop) + total_price += 499 + switch(dev_disk) + if(1) // Basic(128GQ) + if(fabricate) + fabricated_laptop.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive(fabricated_laptop) + if(2) // Upgraded(256GQ) + if(fabricate) + fabricated_laptop.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(fabricated_laptop) + total_price += 99 + if(3) // Advanced(512GQ) + if(fabricate) + fabricated_laptop.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(fabricated_laptop) + total_price += 299 + switch(dev_netcard) + if(1) // Basic(Short-Range) + if(fabricate) + fabricated_laptop.network_card = new/obj/item/weapon/computer_hardware/network_card(fabricated_laptop) + total_price += 99 + if(2) // Advanced (Long Range) + if(fabricate) + fabricated_laptop.network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(fabricated_laptop) + total_price += 299 + if(dev_tesla) + total_price += 399 + if(fabricate) + fabricated_laptop.tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(fabricated_laptop) + if(dev_nanoprint) + total_price += 99 + if(fabricate) + fabricated_laptop.nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(fabricated_laptop) + if(dev_card) + total_price += 199 + if(fabricate) + fabricated_laptop.card_slot = new/obj/item/weapon/computer_hardware/card_slot(fabricated_laptop) + + return total_price + else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this. + if(fabricate) + fabricated_tablet = new(src) + fabricated_tablet.processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(fabricated_tablet) + total_price = 199 + switch(dev_battery) + if(1) // Basic(300C) + if(fabricate) + fabricated_tablet.battery_module = new/obj/item/weapon/computer_hardware/battery_module/nano(fabricated_tablet) + if(2) // Upgraded(500C) + if(fabricate) + fabricated_tablet.battery_module = new/obj/item/weapon/computer_hardware/battery_module/micro(fabricated_tablet) + total_price += 199 + if(3) // Advanced(750C) + if(fabricate) + fabricated_tablet.battery_module = new/obj/item/weapon/computer_hardware/battery_module(fabricated_tablet) + total_price += 499 + switch(dev_disk) + if(1) // Basic(32GQ) + if(fabricate) + fabricated_tablet.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/micro(fabricated_tablet) + if(2) // Upgraded(64GQ) + if(fabricate) + fabricated_tablet.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(fabricated_tablet) + total_price += 99 + if(3) // Advanced(128GQ) + if(fabricate) + fabricated_tablet.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive(fabricated_tablet) + total_price += 299 + switch(dev_netcard) + if(1) // Basic(Short-Range) + if(fabricate) + fabricated_tablet.network_card = new/obj/item/weapon/computer_hardware/network_card(fabricated_tablet) + total_price += 99 + if(2) // Advanced (Long Range) + if(fabricate) + fabricated_tablet.network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(fabricated_tablet) + total_price += 299 + if(dev_nanoprint) + total_price += 99 + if(fabricate) + fabricated_tablet.nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(fabricated_tablet) + if(dev_card) + total_price += 199 + if(fabricate) + fabricated_tablet.card_slot = new/obj/item/weapon/computer_hardware/card_slot(fabricated_tablet) + if(dev_tesla) + total_price += 399 + if(fabricate) + fabricated_tablet.tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(fabricated_tablet) + return total_price + return 0 + + + + + +/obj/machinery/lapvend/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["pick_device"]) + if(state) // We've already picked a device type + return 0 + devtype = text2num(href_list["pick_device"]) + state = 1 + fabricate_and_recalc_price(0) + return 1 + if(href_list["clean_order"]) + reset_order() + return 1 + if((state != 1) && devtype) // Following IFs should only be usable when in the Select Loadout mode + return 0 + if(href_list["confirm_order"]) + state = 2 // Wait for ID swipe for payment processing + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_cpu"]) + dev_cpu = text2num(href_list["hw_cpu"]) + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_battery"]) + dev_battery = text2num(href_list["hw_battery"]) + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_disk"]) + dev_disk = text2num(href_list["hw_disk"]) + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_netcard"]) + dev_netcard = text2num(href_list["hw_netcard"]) + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_tesla"]) + dev_tesla = text2num(href_list["hw_tesla"]) + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_nanoprint"]) + dev_nanoprint = text2num(href_list["hw_nanoprint"]) + fabricate_and_recalc_price(0) + return 1 + if(href_list["hw_card"]) + dev_card = text2num(href_list["hw_card"]) + fabricate_and_recalc_price(0) + return 1 + return 0 + +/obj/machinery/lapvend/attack_hand(var/mob/user) + ui_interact(user) + +/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(stat & (BROKEN | NOPOWER | MAINT)) + if(ui) + ui.close() + return 0 + + var/list/data[0] + data["state"] = state + if(state == 1) + data["devtype"] = devtype + data["hw_battery"] = dev_battery + data["hw_disk"] = dev_disk + data["hw_netcard"] = dev_netcard + data["hw_tesla"] = dev_tesla + data["hw_nanoprint"] = dev_nanoprint + data["hw_card"] = dev_card + data["hw_cpu"] = dev_cpu + if(state == 1 || state == 2) + data["totalprice"] = total_price + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 400) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + +obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/obj/item/weapon/card/id/I = W.GetID() + // Awaiting payment state + if(state == 2) + if(process_payment(I,W)) + fabricate_and_recalc_price(1) + if((devtype == 1) && fabricated_laptop) + if(fabricated_laptop.battery_module) + fabricated_laptop.battery_module.charge_to_full() + fabricated_laptop.forceMove(src.loc) + fabricated_laptop.screen_on = 0 + fabricated_laptop.anchored = 0 + fabricated_laptop.update_icon() + fabricated_laptop.update_verbs() + fabricated_laptop = null + else if((devtype == 2) && fabricated_tablet) + if(fabricated_tablet.battery_module) + fabricated_tablet.battery_module.charge_to_full() + fabricated_tablet.forceMove(src.loc) + fabricated_tablet.update_verbs() + fabricated_tablet = null + ping("Enjoy your new product!") + state = 3 + return 1 + return 0 + return ..() + + +// Simplified payment processing, returns 1 on success. +/obj/machinery/lapvend/proc/process_payment(var/obj/item/weapon/card/id/I, var/obj/item/ID_container) + if(I==ID_container || ID_container == null) + visible_message("\The [usr] swipes \the [I] through \the [src].") + else + visible_message("\The [usr] swipes \the [ID_container] through \the [src].") + var/datum/money_account/customer_account = get_account(I.associated_account_number) + if (!customer_account || customer_account.suspended) + ping("Connection error. Unable to connect to account.") + return 0 + + if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + var/attempt_pin = input("Enter pin code", "Vendor transaction") as num + customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) + + if(!customer_account) + ping("Unable to access account: incorrect credentials.") + return 0 + + if(total_price > customer_account.money) + ping("Insufficient funds in account.") + return 0 + else + customer_account.money -= total_price + var/datum/transaction/T = new() + T.target_name = "Computer Manufacturer (via [src.name])" + T.purpose = "Purchase of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"]." + T.amount = total_price + T.source_terminal = src.name + T.date = current_date_string + T.time = stationtime2text() + customer_account.transaction_log.Add(T) + return 1 \ No newline at end of file diff --git a/code/modules/multi-tile/multi-tile.dm b/code/modules/multi-tile/multi-tile.dm new file mode 100644 index 0000000000..b8dd9ef512 --- /dev/null +++ b/code/modules/multi-tile/multi-tile.dm @@ -0,0 +1,28 @@ +/* + * This is the home of multi-tile movement checks, and thus here be dragons. You are warned. + */ + +/atom/movable/proc/check_multi_tile_move_density_dir(var/stepdir) + if(!locs || !locs.len) + return TRUE + + if(bound_height > 32 || bound_width > 32) + var/safe_move = TRUE + var/list/checked_turfs = list() + for(var/turf/T in locs) + var/turf/Tcheck = get_step(T, stepdir) + if(Tcheck in checked_turfs) + continue + if(Tcheck in locs) + checked_turfs |= Tcheck + continue + if(!(Tcheck in locs)) + if(!T.Exit(src, Tcheck)) + safe_move = FALSE + if(!Tcheck.Enter(src, T)) + safe_move = FALSE + checked_turfs |= Tcheck + if(!safe_move) + break + return safe_move + return TRUE diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/structures.dm index a13a1457a3..331588183e 100644 --- a/code/modules/multiz/structures.dm +++ b/code/modules/multiz/structures.dm @@ -111,7 +111,7 @@ if(!A.CanPass(M, M.loc, 1.5, 0)) to_chat(M, "\The [A] is blocking \the [src].") return FALSE - return M.Move(T) + return M.forceMove(T) //VOREStation Edit - Fixes adminspawned ladders /obj/structure/ladder/CanPass(obj/mover, turf/source, height, airflow) return airflow || !density diff --git a/code/modules/multiz/structures_vr.dm b/code/modules/multiz/structures_vr.dm new file mode 100644 index 0000000000..e55b0eae5a --- /dev/null +++ b/code/modules/multiz/structures_vr.dm @@ -0,0 +1,79 @@ +/obj/structure/portal_subtle + name = "portal" + desc = "Looks unstable. Best to test it with the clown." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "portal" + density = 1 + unacidable = 1//Can't destroy energy portals. + var/failchance = 0 + anchored = 1 + var/obj/structure/portal_subtle/target + +/obj/structure/portal_subtle/Destroy() + if(target) + target.target = null + target = null + return ..() + +/obj/structure/portal_subtle/Bumped(mob/M as mob|obj) + if(istype(M,/mob) && !(istype(M,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(M) + return + return + +/obj/structure/portal_subtle/Crossed(AM as mob|obj) + if(istype(AM,/mob) && !(istype(AM,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(AM) + return + return + +/obj/structure/portal_subtle/attack_hand(mob/user as mob) + if(istype(user) && !(istype(user,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(user) + return + return + +/obj/structure/portal_subtle/proc/teleport(atom/movable/M as mob|obj) + if(istype(M, /obj/effect)) //sparks don't teleport + return + if (M.anchored&&istype(M, /obj/mecha)) + return + if (icon_state == "portal1") + return + if (!( target )) + qdel(src) + return + if (istype(target, /obj/structure/portal_subtle)) + qdel(src) + return + if (istype(M, /atom/movable)) + if(prob(failchance)) //oh dear a problem, put em in deep space + src.icon_state = "portal1" + do_noeffect_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0) + else + do_noeffect_teleport(M, target, 0) ///You will appear on the beacon + +/obj/structure/portal_gateway + name = "portal" + desc = "Looks unstable. Best to test it with the clown." + icon = 'icons/obj/stationobjs_vr.dmi' + icon_state = "portalgateway" + density = 1 + unacidable = 1//Can't destroy energy portals. + anchored = 1 + +/obj/structure/portal_gateway/Bumped(mob/M as mob|obj) + if(istype(M,/mob) && !(istype(M,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + var/obj/effect/landmark/dest = pick(eventdestinations) + if(dest) + M << 'sound/effects/phasein.ogg' + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + M.forceMove(dest.loc) + return diff --git a/code/modules/nano/interaction/base.dm b/code/modules/nano/interaction/base.dm index e48022e2cc..eb3e9ae5c1 100644 --- a/code/modules/nano/interaction/base.dm +++ b/code/modules/nano/interaction/base.dm @@ -30,7 +30,7 @@ /mob/living/silicon/robot/shared_nano_interaction() . = STATUS_INTERACTIVE - if(cell.charge <= 0) + if(!has_power) return STATUS_CLOSE if(lockdown) . = STATUS_DISABLED diff --git a/code/modules/nano/modules/human_appearance.dm b/code/modules/nano/modules/human_appearance.dm index 3d790b1194..fd2ed900f2 100644 --- a/code/modules/nano/modules/human_appearance.dm +++ b/code/modules/nano/modules/human_appearance.dm @@ -100,7 +100,7 @@ return generate_data(check_whitelist, whitelist, blacklist) - var/data[0] + var/list/data = host.initial_data() data["specimen"] = owner.species.name data["gender"] = owner.gender diff --git a/code/modules/nano/modules/nano_module.dm b/code/modules/nano/modules/nano_module.dm index 8ced14f4c5..90c213da1c 100644 --- a/code/modules/nano/modules/nano_module.dm +++ b/code/modules/nano/modules/nano_module.dm @@ -1,16 +1,64 @@ /datum/nano_module var/name - var/host + var/datum/host + var/datum/topic_manager/topic_manager + var/list/using_access -/datum/nano_module/New(var/host) - src.host = host - -/datum/nano_module/Destroy() - host = null - return ..() +/datum/nano_module/New(var/datum/host, var/topic_manager) + ..() + src.host = host.nano_host() + src.topic_manager = topic_manager /datum/nano_module/nano_host() return host ? host : src /datum/nano_module/proc/can_still_topic(var/datum/topic_state/state = default_state) return CanUseTopic(usr, state) == STATUS_INTERACTIVE + +/datum/nano_module/proc/check_eye(var/mob/user) + return -1 + +/datum/nano_module/proc/check_access(var/mob/user, var/access) + if(!access) + return 1 + + if(using_access) + if(access in using_access) + return 1 + else + return 0 + + if(!istype(user)) + return 0 + + var/obj/item/weapon/card/id/I = user.GetIdCard() + if(!I) + return 0 + + if(access in I.access) + return 1 + + return 0 + +/datum/nano_module/Topic(href, href_list) + if(topic_manager && topic_manager.Topic(href, href_list)) + return TRUE + . = ..() + +/datum/nano_module/proc/print_text(var/text, var/mob/user) + var/obj/item/modular_computer/MC = nano_host() + if(istype(MC)) + if(!MC.nano_printer) + to_chat(user, "Error: No printer detected. Unable to print document.") + return + + if(!MC.nano_printer.print_text(text)) + to_chat(user, "Error: Printer was unable to print the document. It may be out of paper.") + else + to_chat(user, "Error: Unable to detect compatible printer interface. Are you running NTOSv2 compatible system?") + +/datum/proc/initial_data() + return list() + +/datum/proc/update_layout() + return FALSE diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index 43f98e3361..e6df874652 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -111,6 +111,7 @@ nanoui is used to open and update nano browser uis add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs add_stylesheet("shared.css") // this CSS sheet is common to all UIs + add_stylesheet("shared_vr.css") // VOREStation Add add_stylesheet("icons.css") // this CSS sheet is common to all UIs /** @@ -401,6 +402,11 @@ nanoui is used to open and update nano browser uis if(!user.client) return + // An attempted fix to UIs sometimes locking up spamming runtime errors due to src_object being null for whatever reason. + // This hard-deletes the UI, preventing the device that uses the UI from being locked up permanently. + if(!src_object) + del(src) + var/window_size = "" if (width && height) window_size = "size=[width]x[height];" diff --git a/code/modules/nifsoft/nifsoft.dm b/code/modules/nifsoft/nifsoft.dm index b4791003d0..6f110de856 100644 --- a/code/modules/nifsoft/nifsoft.dm +++ b/code/modules/nifsoft/nifsoft.dm @@ -165,15 +165,19 @@ return ..() ///////////////// -// A NIFSoft Disk +// A NIFSoft Uploader /obj/item/weapon/disk/nifsoft - name = "NIFSoft Disk" + name = "NIFSoft Uploader" desc = "It has a small label: \n\ - \"Portable NIFSoft Disk. \n\ - Insert directly into brain.\"" - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk2" - item_state = "card-id" + \"Portable NIFSoft Installation Media. \n\ + Align ocular port with eye socket and depress red plunger.\"" + icon = 'icons/obj/nanomods.dmi' + icon_state = "medical" + item_state = "nanomod" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_vr.dmi', + ) w_class = ITEMSIZE_SMALL var/datum/nifsoft/stored = null @@ -188,7 +192,7 @@ var/mob/living/carbon/human/Hu = user if(!Ht.nif || Ht.nif.stat != NIF_WORKING) - to_chat(user,"Either they don't have a NIF, or the disk can't connect.") + to_chat(user,"Either they don't have a NIF, or the uploader can't connect.") return var/extra = extra_params() @@ -197,9 +201,18 @@ else Ht.visible_message("[Hu] begins uploading [src] into [Ht]!","[Hu] is uploading [src] into you!") - if(A == user || do_after(Hu,10 SECONDS,Ht)) + icon_state = "[initial(icon_state)]-animate" //makes it play the item animation upon using on a valid target + update_icon() + + if(A == user && do_after(Hu,1 SECONDS,Ht)) new stored(Ht.nif,extra) qdel(src) + else if(A != user && do_after(Hu,10 SECONDS,Ht)) + new stored(Ht.nif,extra) + qdel(src) + else + icon_state = "[initial(icon_state)]" //If it fails to apply to a valid target and doesn't get deleted, reset its icon state + update_icon() //So disks can pass fancier stuff. /obj/item/weapon/disk/nifsoft/proc/extra_params() @@ -208,8 +221,14 @@ // Compliance Disk // /obj/item/weapon/disk/nifsoft/compliance - name = "NIFSoft Disk (Compliance)" + name = "NIFSoft Uploader (Compliance)" desc = "Wow, adding laws to people? That seems illegal. It probably is. Okay, it really is." + icon_state = "compliance" + item_state = "healthanalyzer" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand.dmi', + slot_r_hand_str = 'icons/mob/items/righthand.dmi', + ) stored = /datum/nifsoft/compliance var/laws @@ -233,19 +252,20 @@ // Security Disk // /obj/item/weapon/disk/nifsoft/security - name = "NIFSoft Disk - Security" + name = "NIFSoft Uploader - Security" desc = "Contains free NIFSofts useful for security members.\n\ It has a small label: \n\ - \"Portable NIFSoft Disk. \n\ - Insert directly into brain.\"" + \"Portable NIFSoft Installation Media. \n\ + Align ocular port with eye socket and depress red plunger.\"" + icon_state = "security" stored = /datum/nifsoft/package/security /datum/nifsoft/package/security software = list(/datum/nifsoft/ar_sec,/datum/nifsoft/flashprot) /obj/item/weapon/storage/box/nifsofts_security - name = "security nifsoft disks" + name = "security nifsoft uploaders" desc = "A box of free nifsofts for security employees." icon_state = "disk_kit" @@ -256,19 +276,20 @@ // Engineering Disk // /obj/item/weapon/disk/nifsoft/engineering - name = "NIFSoft Disk - Engineering" + name = "NIFSoft Uploader - Engineering" desc = "Contains free NIFSofts useful for engineering members.\n\ It has a small label: \n\ - \"Portable NIFSoft Disk. \n\ - Insert directly into brain.\"" + \"Portable NIFSoft Installation Media. \n\ + Align ocular port with eye socket and depress red plunger.\"" + icon_state = "engineering" stored = /datum/nifsoft/package/engineering /datum/nifsoft/package/engineering software = list(/datum/nifsoft/ar_eng,/datum/nifsoft/alarmmonitor,/datum/nifsoft/uvblocker) /obj/item/weapon/storage/box/nifsofts_engineering - name = "engineering nifsoft disks" + name = "engineering nifsoft uploaders" desc = "A box of free nifsofts for engineering employees." icon_state = "disk_kit" @@ -279,11 +300,11 @@ // Medical Disk // /obj/item/weapon/disk/nifsoft/medical - name = "NIFSoft Disk - Medical" + name = "NIFSoft Uploader - Medical" desc = "Contains free NIFSofts useful for medical members.\n\ It has a small label: \n\ - \"Portable NIFSoft Disk. \n\ - Insert directly into brain.\"" + \"Portable NIFSoft Installation Media. \n\ + Align ocular port with eye socket and depress red plunger.\"" stored = /datum/nifsoft/package/medical @@ -291,7 +312,7 @@ software = list(/datum/nifsoft/ar_med,/datum/nifsoft/crewmonitor) /obj/item/weapon/storage/box/nifsofts_medical - name = "medical nifsoft disks" + name = "medical nifsoft uploaders" desc = "A box of free nifsofts for medical employees." icon_state = "disk_kit" @@ -302,19 +323,20 @@ // Mining Disk // /obj/item/weapon/disk/nifsoft/mining - name = "NIFSoft Disk - Mining" + name = "NIFSoft Uploader - Mining" desc = "Contains free NIFSofts useful for mining members.\n\ It has a small label: \n\ - \"Portable NIFSoft Disk. \n\ - Insert directly into brain.\"" + \"Portable NIFSoft Installation Media. \n\ + Align ocular port with eye socket and depress red plunger.\"" + icon_state = "mining" stored = /datum/nifsoft/package/mining /datum/nifsoft/package/mining software = list(/datum/nifsoft/material,/datum/nifsoft/spare_breath) /obj/item/weapon/storage/box/nifsofts_mining - name = "mining nifsoft disks" + name = "mining nifsoft uploaders" desc = "A box of free nifsofts for mining employees." icon_state = "disk_kit" diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm index 09d23669b3..9f46eac243 100644 --- a/code/modules/nifsoft/software/13_soulcatcher.dm +++ b/code/modules/nifsoft/software/13_soulcatcher.dm @@ -270,10 +270,12 @@ var/obj/item/device/nif/nif var/datum/nifsoft/soulcatcher/soulcatcher + var/identifying_gender /mob/living/carbon/brain/caught_soul/Login() ..() plane_holder.set_vis(VIS_AUGMENTED, TRUE) + identifying_gender = client.prefs.identifying_gender /mob/living/carbon/brain/caught_soul/Destroy() if(soulcatcher) diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index bb38043767..198b88f88c 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -117,7 +117,7 @@ var/const/CE_STABLE_THRESHOLD = 0.5 src << "You feel extremely [word]" else if(blood_volume >= BLOOD_VOLUME_SURVIVE) adjustOxyLoss(5 * dmg_coef) - adjustToxLoss(3 * dmg_coef) +// adjustToxLoss(3 * dmg_coef) if(prob(15)) var/word = pick("dizzy","woosey","faint") src << "You feel extremely [word]" diff --git a/code/modules/organs/internal/augment.dm b/code/modules/organs/internal/augment.dm new file mode 100644 index 0000000000..0c168d1679 --- /dev/null +++ b/code/modules/organs/internal/augment.dm @@ -0,0 +1,177 @@ +/* + * Augments. This file contains the base, and organic-targeting augments. + */ + +/obj/item/organ/internal/augment + name = "augment" + + icon_state = "cell_bay" + + parent_organ = BP_TORSO + + organ_verbs = list(/mob/living/carbon/human/proc/augment_menu) // Verbs added by the organ when present in the body. + target_parent_classes = list() // Is the parent supposed to be organic, robotic, assisted? + forgiving_class = FALSE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. + + var/obj/item/integrated_object // Objects held by the organ, used for re-usable, deployable things. + var/integrated_object_type // Object type the organ will spawn. + var/target_slot = null + + var/silent_deploy = FALSE + + var/image/my_radial_icon = null + var/radial_icon = null // DMI for the augment's radial icon. + var/radial_name = null // The augment's name in the Radial Menu. + var/radial_state = null // Icon state for the augment's radial icon. + + var/aug_cooldown = 30 SECONDS + var/last_activate = null + +/obj/item/organ/internal/augment/Initialize() + ..() + + setup_radial_icon() + + if(integrated_object_type) + integrated_object = new integrated_object_type(src) + integrated_object.canremove = FALSE + +/obj/item/organ/internal/augment/proc/setup_radial_icon() + if(!radial_icon) + radial_icon = icon + if(!radial_name) + radial_name = name + if(!radial_state) + radial_state = icon_state + my_radial_icon = image(icon = radial_icon, icon_state = radial_state) + +/obj/item/organ/internal/augment/handle_organ_mod_special(var/removed = FALSE) + if(removed && integrated_object && integrated_object.loc != src) + if(isliving(integrated_object.loc)) + var/mob/living/L = integrated_object.loc + L.drop_from_inventory(integrated_object) + integrated_object.forceMove(src) + ..(removed) + +/obj/item/organ/internal/augment/proc/augment_action() + if(!owner) + return + + if(aug_cooldown) + if(last_activate <= world.time + aug_cooldown) + last_activate = world.time + else + return + + var/item_to_equip = integrated_object + if(!item_to_equip && integrated_object_type) + item_to_equip = integrated_object_type + + if(ispath(item_to_equip)) + owner.equip_augment_item(target_slot, item_to_equip, silent_deploy, FALSE) + else if(item_to_equip) + owner.equip_augment_item(target_slot, item_to_equip, silent_deploy, FALSE, src) + +/* + * The delicate handling of augment-controlled items. + */ + +// Attaches to the end of dropped items' code. + +/obj/item + var/destroy_on_drop = FALSE // Used by augments to determine if the item should destroy itself when dropped, or return to its master. + var/obj/item/organ/my_augment = null // Used to reference the object's host organ. + +/obj/item/dropped(mob/user) + . = ..() + if(src) + if(destroy_on_drop && !QDELETED(src)) + qdel(src) + return + if(my_augment) + forceMove(my_augment) + +/* + * Human-specific mob procs. + */ + +// The next two procs simply handle the radial menu for augment activation. + +/mob/living/carbon/human/proc/augment_menu() + set name = "Open Augment Menu" + set desc = "Toggle your augment menu." + set category = "Augments" + + enable_augments(usr) + +/mob/living/carbon/human/proc/enable_augments(var/mob/living/user) + var/list/options = list() + + var/list/present_augs = list() + + for(var/obj/item/organ/internal/augment/Aug in internal_organs) + if(Aug.my_radial_icon && !Aug.is_broken() && Aug.check_verb_compatability()) + present_augs[Aug.radial_name] = Aug + + for(var/augname in present_augs) + var/obj/item/organ/internal/augment/iconsource = present_augs[augname] + options[augname] = iconsource.my_radial_icon + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(user, src, options) + + if(!isnull(choice) && options[choice]) + var/obj/item/organ/internal/augment/A = present_augs[choice] + A.augment_action(user) + +/* equip_augment_item + * Used to equip an organ's augment items when possible. + * slot is the target equip slot, if it's not a generic either-hand deployable, + * equipping is either the target object, or a path for the target object, + * destroy_on_drop is the default value for the object to be deleted if it is removed from their person, if equipping is a path, however, this will be set to TRUE, + * cling_to_organ is a reference to the organ object itself, so they can easily return to their organ when removed by any means. + */ + +/mob/living/carbon/human/proc/equip_augment_item(var/slot, var/obj/item/equipping = null, var/make_sound = TRUE, var/destroy_on_drop = FALSE, var/obj/item/organ/cling_to_organ = null) + if(!ishuman(src)) + return 0 + + if(!equipping) + return 0 + + var/mob/living/carbon/human/M = src + + if((slot == slot_l_hand && l_hand) || (slot == slot_r_hand && r_hand)) + to_chat(M,"Your hand is full. Drop something first.") + return 0 + + var/del_if_failure = destroy_on_drop + + if(ispath(equipping)) + del_if_failure = TRUE + equipping = new equipping(src) + + if(!slot) + put_in_any_hand_if_possible(equipping, del_if_failure) + + else + if(slot_is_accessible(slot, equipping, src)) + equip_to_slot(equipping, slot, 1, 1) + else if(destroy_on_drop || del_if_failure) + qdel(equipping) + return 0 + + if(cling_to_organ) // Does the object automatically return to the organ? + equipping.my_augment = cling_to_organ + + if(make_sound) + playsound(src, 'sound/items/change_jaws.ogg', 30, 1) + + if(equipping.loc != src) + equipping.dropped() + + return 1 diff --git a/code/modules/organs/internal/augment/armmounted.dm b/code/modules/organs/internal/augment/armmounted.dm new file mode 100644 index 0000000000..73751be0d9 --- /dev/null +++ b/code/modules/organs/internal/augment/armmounted.dm @@ -0,0 +1,262 @@ +/* + * Arm mounted augments. + */ + +/obj/item/organ/internal/augment/armmounted + name = "laser rifle implant" + desc = "A large implant that fits into a subject's arm. It deploys a laser-emitting array by some painful means." + + icon_state = "augment_laser" + + w_class = ITEMSIZE_LARGE + + organ_tag = O_AUG_L_FOREARM + + parent_organ = BP_L_ARM + + target_slot = slot_l_hand + + target_parent_classes = list(ORGAN_FLESH, ORGAN_ASSISTED) + + integrated_object_type = /obj/item/weapon/gun/energy/laser/mounted/augment + +/obj/item/organ/internal/augment/armmounted/attackby(obj/item/I as obj, mob/user as mob) + if(I.is_screwdriver()) + switch(organ_tag) + if(O_AUG_L_FOREARM) + organ_tag = O_AUG_R_FOREARM + parent_organ = BP_R_ARM + target_slot = slot_r_hand + if(O_AUG_R_FOREARM) + organ_tag = O_AUG_L_FOREARM + parent_organ = BP_L_ARM + target_slot = slot_l_hand + to_chat(user, "You swap \the [src]'s servos to install neatly into \the lower [parent_organ] mount.") + return + + . = ..() + +/obj/item/organ/internal/augment/armmounted/taser + name = "taser implant" + desc = "A large implant that fits into a subject's arm. It deploys a taser-emitting array by some painful means." + + icon_state = "augment_taser" + + integrated_object_type = /obj/item/weapon/gun/energy/taser/mounted/augment + +/obj/item/organ/internal/augment/armmounted/dartbow + name = "crossbow implant" + desc = "A small implant that fits into a subject's arm. It deploys a dart launching mechanism through the flesh through unknown means." + + icon_state = "augment_dart" + + w_class = ITEMSIZE_SMALL + + integrated_object_type = /obj/item/weapon/gun/energy/crossbow + +// Wrist-or-hand-mounted implant + +/obj/item/organ/internal/augment/armmounted/hand + name = "resonant analyzer implant" + desc = "An augment that fits neatly into the hand, useful for determining the usefulness of an object for research." + icon_state = "augment_box" + + w_class = ITEMSIZE_SMALL + + integrated_object_type = /obj/item/weapon/portable_scanner + +/obj/item/organ/internal/augment/armmounted/hand/attackby(obj/item/I as obj, mob/user as mob) + if(I.is_screwdriver()) + switch(organ_tag) + if(O_AUG_L_HAND) + organ_tag = O_AUG_R_HAND + parent_organ = BP_R_HAND + target_slot = slot_r_hand + if(O_AUG_R_HAND) + organ_tag = O_AUG_L_HAND + parent_organ = BP_L_HAND + target_slot = slot_l_hand + to_chat(user, "You swap \the [src]'s servos to install neatly into \the upper [parent_organ] mount.") + return + + . = ..() + +/obj/item/organ/internal/augment/armmounted/hand/sword + name = "energy blade implant" + + integrated_object_type = /obj/item/weapon/melee/energy/sword + +/* + * Shoulder augment. + */ + +/obj/item/organ/internal/augment/armmounted/shoulder + name = "shoulder augment" + desc = "A large implant that fits into a subject's arm. It looks kind of like a skeleton." + + icon_state = "augment_armframe" + + organ_tag = O_AUG_R_UPPERARM + + w_class = ITEMSIZE_HUGE + + integrated_object_type = null + +/obj/item/organ/internal/augment/armmounted/shoulder/attackby(obj/item/I as obj, mob/user as mob) + if(I.is_screwdriver()) + switch(organ_tag) + if(O_AUG_L_UPPERARM) + organ_tag = O_AUG_R_UPPERARM + parent_organ = BP_R_ARM + target_slot = slot_r_hand + if(O_AUG_R_UPPERARM) + organ_tag = O_AUG_L_UPPERARM + parent_organ = BP_L_ARM + target_slot = slot_l_hand + to_chat(user, "You swap \the [src]'s servos to install neatly into \the upper [parent_organ] mount.") + return + + . = ..() + +/obj/item/organ/internal/augment/armmounted/shoulder/surge + name = "muscle overclocker" + + aug_cooldown = 1.5 MINUTES + +/obj/item/organ/internal/augment/armmounted/shoulder/surge/augment_action() + if(!owner) + return + + if(aug_cooldown) + if(last_activate <= world.time + aug_cooldown) + last_activate = world.time + else + return + + if(istype(owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner + H.add_modifier(/datum/modifier/melee_surge, 0.75 MINUTES) + +// The toolkit / multi-tool implant. + +/obj/item/organ/internal/augment/armmounted/shoulder/multiple + name = "rotary toolkit" + desc = "A large implant that fits into a subject's arm. It deploys an array of tools by some painful means." + + icon_state = "augment_toolkit" + + organ_tag = O_AUG_R_UPPERARM + + w_class = ITEMSIZE_HUGE + + integrated_object_type = null + + toolspeed = 0.8 + + var/list/integrated_tools = list( + /obj/item/weapon/tool/screwdriver = null, + /obj/item/weapon/tool/wrench = null, + /obj/item/weapon/tool/crowbar = null, + /obj/item/weapon/tool/wirecutters = null, + /obj/item/device/multitool = null, + /obj/item/stack/cable_coil/gray = null, + /obj/item/weapon/tape_roll = null + ) + + var/list/integrated_tools_by_name + + var/list/integrated_tool_images + + var/list/synths + + var/list/synth_types = list( + /datum/matter_synth/wire + ) + +/obj/item/organ/internal/augment/armmounted/shoulder/multiple/Initialize() + ..() + + if(integrated_object) + integrated_tools[integrated_object_type] = integrated_object + + if(integrated_tools && integrated_tools.len) + + integrated_tools_by_name = list() + + integrated_tool_images = list() + + if(synth_types) + synths = list() + for(var/datumpath in synth_types) + var/datum/matter_synth/MS = new datumpath + synths += MS + + for(var/path in integrated_tools) + if(!integrated_tools[path]) + integrated_tools[path] = new path(src) + var/obj/item/I = integrated_tools[path] + I.canremove = FALSE + I.toolspeed = toolspeed + I.my_augment = src + I.name = "integrated [I.name]" + + for(var/tool in integrated_tools) + var/obj/item/Tool = integrated_tools[tool] + if(istype(Tool, /obj/item/stack)) + var/obj/item/stack/S = Tool + S.synths = synths + S.uses_charge = synths.len + integrated_tools_by_name[Tool.name] = Tool + integrated_tool_images[Tool.name] = image(icon = Tool.icon, icon_state = Tool.icon_state) + +/obj/item/organ/internal/augment/armmounted/shoulder/multiple/handle_organ_proc_special() + ..() + + if(!owner || is_bruised() || !synths) + return + + if(prob(20)) + for(var/datum/matter_synth/MS in synths) + MS.add_charge(MS.recharge_rate) + +/obj/item/organ/internal/augment/armmounted/shoulder/multiple/augment_action() + if(!owner) + return + + var/list/options = list() + + for(var/Iname in integrated_tools_by_name) + options[Iname] = integrated_tool_images[Iname] + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(owner, owner, options) + + integrated_object = integrated_tools_by_name[choice] + + ..() + +/obj/item/organ/internal/augment/armmounted/shoulder/multiple/medical + name = "rotary medical kit" + icon_state = "augment_medkit" + integrated_object_type = null + + integrated_tools = list( + /obj/item/weapon/surgical/hemostat = null, + /obj/item/weapon/surgical/retractor = null, + /obj/item/weapon/surgical/cautery = null, + /obj/item/weapon/surgical/surgicaldrill = null, + /obj/item/weapon/surgical/scalpel = null, + /obj/item/weapon/surgical/circular_saw = null, + /obj/item/weapon/surgical/bonegel = null, + /obj/item/weapon/surgical/FixOVein = null, + /obj/item/weapon/surgical/bonesetter = null, + /obj/item/stack/medical/crude_pack = null + ) + + synth_types = list( + /datum/matter_synth/bandage + ) diff --git a/code/modules/organs/internal/augment/bio.dm b/code/modules/organs/internal/augment/bio.dm new file mode 100644 index 0000000000..543394fcde --- /dev/null +++ b/code/modules/organs/internal/augment/bio.dm @@ -0,0 +1,104 @@ +// The base organic-targeting augment. + +/obj/item/organ/internal/augment/bioaugment + name = "bioaugmenting implant" + + icon_state = "augment_hybrid" + dead_icon = "augment_hybrid_dead" + + robotic = ORGAN_ASSISTED + target_parent_classes = list(ORGAN_FLESH) + +/* Jensen Shades. Your vision can be augmented. + * This, technically, no longer needs its unique organ verb, however I have chosen to leave it for posterity + * in the event it needs to be referenced, while still remaining perfectly functional with either system. + */ + +/obj/item/organ/internal/augment/bioaugment/thermalshades + name = "integrated thermolensing implant" + desc = "A miniscule implant that houses a pair of thermolensed sunglasses. Don't ask how they deploy, you don't want to know." + icon_state = "augment_shades" + dead_icon = "augment_shades_dead" + + w_class = ITEMSIZE_TINY + + organ_tag = O_AUG_EYES + + robotic = ORGAN_ROBOT + + parent_organ = BP_HEAD + + organ_verbs = list( + /mob/living/carbon/human/proc/augment_menu, + /mob/living/carbon/human/proc/toggle_shades) + + integrated_object_type = /obj/item/clothing/glasses/hud/security/jensenshades + +/obj/item/organ/internal/augment/bioaugment/thermalshades/augment_action() + if(!owner) + return + + owner.toggle_shades() + +// Here for posterity and example. +/mob/living/carbon/human/proc/toggle_shades() + set name = "Toggle Integrated Thermoshades" + set desc = "Toggle your flash-proof, thermal-integrated sunglasses." + set category = "Augments" + + var/obj/item/organ/internal/augment/aug = internal_organs_by_name[O_AUG_EYES] + + if(glasses) + if(aug && aug.integrated_object == glasses) + drop_from_inventory(glasses) + aug.integrated_object.forceMove(aug) + if(!glasses) + to_chat(src, "Your [aug.integrated_object] retract into your skull.") + else if(!istype(glasses, /obj/item/clothing/glasses/hud/security/jensenshades)) + to_chat(src, "\The [glasses] block your shades from deploying.") + else if(istype(glasses, /obj/item/clothing/glasses/hud/security/jensenshades)) + var/obj/item/G = glasses + if(G.canremove) + to_chat(src, "\The [G] are not your integrated shades.") + else + drop_from_inventory(G) + to_chat(src, "\The [G] retract into your skull.") + qdel(G) + + else + if(aug && aug.integrated_object) + to_chat(src, "Your [aug.integrated_object] deploy.") + equip_to_slot(aug.integrated_object, slot_glasses, 0, 1) + if(!glasses || glasses != aug.integrated_object) + aug.integrated_object.forceMove(aug) + else + var/obj/item/clothing/glasses/hud/security/jensenshades/J = new(get_turf(src)) + equip_to_slot(J, slot_glasses, 1, 1) + to_chat(src, "Your [aug.integrated_object] deploy.") + +/obj/item/organ/internal/augment/bioaugment/sprint_enhance + name = "locomotive optimization implant" + desc = "A chunk of meat and metal that can manage an individual's leg musculature." + + organ_tag = O_AUG_PELVIC + + parent_organ = BP_GROIN + + target_parent_classes = list(ORGAN_FLESH, ORGAN_ASSISTED) + + aug_cooldown = 2 MINUTES + +/obj/item/organ/internal/augment/bioaugment/sprint_enhance/augment_action() + if(!owner) + return + + if(aug_cooldown) + if(last_activate <= world.time + aug_cooldown) + last_activate = world.time + else + return + + if(istype(owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner + H.add_modifier(/datum/modifier/sprinting, 1 MINUTES) + diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 529114fc74..bb5a5cd65c 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -130,6 +130,11 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) target.key = brainmob.key ..() +/obj/item/organ/internal/brain/proc/get_control_efficiency() + . = max(0, 1 - (round(damage / max_damage * 10) / 10)) + + return . + /obj/item/organ/internal/brain/pariah_brain name = "brain remnants" desc = "Did someone tread on this? It looks useless for cloning or cyborgification." diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm index b65b3bc709..62e5fd16d2 100644 --- a/code/modules/organs/internal/heart.dm +++ b/code/modules/organs/internal/heart.dm @@ -9,7 +9,6 @@ var/standard_pulse_level = PULSE_NORM // We run on a normal clock. This is NOT CONNECTED to species heart-rate modifier. - /obj/item/organ/internal/heart/handle_germ_effects() . = ..() //Up should return an infection level as an integer if(!.) return @@ -23,6 +22,10 @@ owner.custom_pain("A stabbing pain rolls through your chest!",1) owner.apply_damage(damage = 25, damagetype = HALLOSS, def_zone = parent_organ) +/obj/item/organ/internal/heart/robotize() + ..() + standard_pulse_level = PULSE_NONE + /obj/item/organ/internal/heart/grey icon_state = "heart_grey-on" dead_icon = "heart_grey-off" diff --git a/code/modules/organs/internal/intestine.dm b/code/modules/organs/internal/intestine.dm new file mode 100644 index 0000000000..ed5319fb27 --- /dev/null +++ b/code/modules/organs/internal/intestine.dm @@ -0,0 +1,23 @@ +/obj/item/organ/internal/intestine + name = "intestine" + icon_state = "intestine" + organ_tag = O_INTESTINE + parent_organ = BP_GROIN + +/obj/item/organ/internal/intestine/handle_germ_effects() + . = ..() //Up should return an infection level as an integer + if(!.) return + + //Viral Gastroenteritis + if (. >= 1) + if(prob(1)) + owner.custom_pain("There's a twisting pain in your abdomen!",1) + owner.vomit() + if (. >= 2) + if(prob(1)) + owner.custom_pain("Your abdomen feels like it's tearing itself apart!",1) + owner.m_intent = "walk" + owner.hud_used.move_intent.icon_state = "walking" + +/obj/item/organ/internal/intestine/xeno + color = "#555555" diff --git a/code/modules/organs/internal/kidneys.dm b/code/modules/organs/internal/kidneys.dm index 571fe4f9e9..2378c0fe99 100644 --- a/code/modules/organs/internal/kidneys.dm +++ b/code/modules/organs/internal/kidneys.dm @@ -22,6 +22,13 @@ else if(is_broken()) owner.adjustToxLoss(0.3 * PROCESS_ACCURACY) +/obj/item/organ/internal/kidneys/handle_organ_proc_special() + . = ..() + + if(owner && owner.getToxLoss() <= owner.getMaxHealth() * 0.1) // If you have less than 10 tox damage (for a human), your kidneys can help purge it. + if(prob(owner.getToxLoss())) + owner.adjustToxLoss(rand(-1,-3)) + /obj/item/organ/internal/kidneys/handle_germ_effects() . = ..() //Up should return an infection level as an integer if(!.) return diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 9966e93aa7..07ef683e06 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -21,6 +21,13 @@ spawn owner.emote("me", 1, "gasps for air!") owner.AdjustLosebreath(15) + if(owner.internal_organs_by_name[O_BRAIN]) // As the brain starts having Trouble, the lungs start malfunctioning. + var/obj/item/organ/internal/brain/Brain = owner.internal_organs_by_name[O_BRAIN] + if(Brain.get_control_efficiency() <= 0.8) + if(prob(4 / max(0.1,Brain.get_control_efficiency()))) + spawn owner.emote("me", 1, "gasps for air!") + owner.AdjustLosebreath(round(3 / max(0.1,Brain.get_control_efficiency()))) + /obj/item/organ/internal/lungs/proc/rupture() var/obj/item/organ/external/parent = owner.get_organ(parent_organ) if(istype(parent)) diff --git a/code/modules/organs/internal/spleen.dm b/code/modules/organs/internal/spleen.dm new file mode 100644 index 0000000000..352a36207f --- /dev/null +++ b/code/modules/organs/internal/spleen.dm @@ -0,0 +1,87 @@ +/obj/item/organ/internal/spleen + name = "spleen" + icon_state = "spleen" + organ_tag = O_SPLEEN + parent_organ = BP_TORSO + w_class = ITEMSIZE_TINY + + var/spleen_tick = 20 // The number of ticks between Spleen cycles. + var/spleen_efficiency = 1 // A multiplier for how efficient this spleen is. + +/obj/item/organ/internal/spleen/process() + ..() + if(!owner) return + + if(owner.life_tick % spleen_tick == 0) + + //High toxins levels are dangerous + if(owner.getToxLoss() >= 30 && !owner.reagents.has_reagent("anti_toxin")) + //Healthy liver suffers on its own + if (src.damage < min_broken_damage) + src.damage += 0.2 * spleen_tick + //Damaged one shares the fun + else + var/obj/item/organ/internal/O = pick(owner.internal_organs) + if(O) + O.damage += 0.2 * spleen_tick + + else if(!src.is_broken()) // If the spleen isn't severely damaged, it can help fight infections. Key word, can. + var/obj/item/organ/external/OEx = pick(owner.organs) + OEx.adjust_germ_level(round(rand(0 * spleen_efficiency,-10 * spleen_efficiency))) + + if(!src.is_bruised() && owner.internal_organs_by_name[O_BRAIN]) // If it isn't bruised, it helps with brain infections. + var/obj/item/organ/internal/brain/B = owner.internal_organs_by_name[O_BRAIN] + B.adjust_germ_level(round(rand(-3 * spleen_efficiency, -10 * spleen_efficiency))) + + //Detox can heal small amounts of damage + if (src.damage && src.damage < src.min_bruised_damage && owner.reagents.has_reagent("anti_toxin")) + src.damage -= 0.2 * spleen_tick * spleen_efficiency + + if(src.damage < 0) + src.damage = 0 + +/obj/item/organ/internal/spleen/handle_germ_effects() + . = ..() //Up should return an infection level as an integer + if(!.) return + + // Low levels can cause pain and haemophilia, high levels can cause brain infections. + if (. >= 1) + if(prob(1)) + owner.custom_pain("There's a sharp pain in your [owner.get_organ(parent_organ)]!",1) + owner.add_modifier(/datum/modifier/trait/haemophilia, 2 MINUTES * spleen_efficiency) + if (. >= 2) + if(prob(1)) + if(owner.getToxLoss() < owner.getMaxHealth() * 0.2 * spleen_efficiency) + owner.adjustToxLoss(2 * spleen_efficiency) + else if(owner.internal_organs_by_name[O_BRAIN]) + var/obj/item/organ/internal/brain/Brain = owner.internal_organs_by_name[O_BRAIN] + Brain.adjust_germ_level(round(rand(5 * spleen_efficiency,20 * spleen_efficiency))) + +/obj/item/organ/internal/spleen/die() + ..() + if(owner) + owner.add_modifier(/datum/modifier/trait/haemophilia, round(15 MINUTES * spleen_efficiency)) + var/obj/item/organ/external/Target = owner.get_organ(parent_organ) + var/datum/wound/W = new /datum/wound/internal_bleeding(round(20 * spleen_efficiency)) + owner.adjustToxLoss(15 * spleen_efficiency) + Target.wounds += W + +/obj/item/organ/internal/spleen/skrell + name = "lymphatic hub" + icon_state = "spleen" + parent_organ = BP_HEAD + spleen_efficiency = 0.5 + +/obj/item/organ/internal/spleen/skrell/Initialize() + ..() + adjust_scale(0.8,0.7) + +/obj/item/organ/internal/spleen/minor + name = "vestigial spleen" + parent_organ = BP_GROIN + spleen_efficiency = 0.3 + spleen_tick = 15 + +/obj/item/organ/internal/spleen/minor/Initialize() + ..() + adjust_scale(0.7) diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm new file mode 100644 index 0000000000..02cf7cb31d --- /dev/null +++ b/code/modules/organs/internal/stomach.dm @@ -0,0 +1,50 @@ +/obj/item/organ/internal/stomach + name = "stomach" + icon_state = "stomach" + organ_tag = O_STOMACH + parent_organ = BP_GROIN + + unacidable = TRUE // Don't melt when holding your acid, dangit. + + var/acidtype = "stomacid" // Incase you want some stomach organ with, say, polyacid instead, or sulphuric. + var/max_acid_volume = 30 + + var/deadly_hold = TRUE // Does the stomach do damage to mobs eaten by its owner? Xenos should probably have this FALSE. + +/obj/item/organ/internal/stomach/Initialize() + ..() + + if(reagents) + reagents.maximum_volume = 30 + else + create_reagents(30) + +/obj/item/organ/internal/stomach/handle_organ_proc_special() + if(owner && istype(owner, /mob/living/carbon/human)) + if(reagents) + if(reagents.total_volume + 2 < max_acid_volume && prob(20)) + reagents.add_reagent(acidtype, rand(1,2)) + + for(var/mob/living/L in owner.stomach_contents) // Splashes mobs inside with acid. Twice as effective as being splashed with the same acid outside the body. + reagents.trans_to(L, 2, 2, 0) + + if(is_broken() && prob(1)) + owner.custom_pain("There's a twisting pain in your abdomen!",1) + owner.vomit(FALSE, TRUE) + +/obj/item/organ/internal/stomach/handle_germ_effects() + . = ..() //Up should return an infection level as an integer + if(!.) return + + //Bacterial Gastroenteritis + if (. >= 1) + if(prob(1)) + owner.custom_pain("There's a twisting pain in your abdomen!",1) + owner.apply_effect(2, AGONY, 0) + if (. >= 2) + if(prob(1) && owner.getToxLoss() < owner.getMaxHealth()*0.2) + owner.adjustToxLoss(3) + owner.vomit(FALSE, TRUE) + +/obj/item/organ/internal/stomach/xeno + color = "#555555" diff --git a/code/modules/organs/internal/voicebox.dm b/code/modules/organs/internal/voicebox.dm index 4de69fe8f2..2cf264f780 100644 --- a/code/modules/organs/internal/voicebox.dm +++ b/code/modules/organs/internal/voicebox.dm @@ -1,7 +1,7 @@ /* * Voicebox/Vocal Synthesizers * TL;DR: Assists with speaking languages that a species doesn't normally have, - * such as EAL. Not standard or organic, because at the moment it's undesireable to completely mute characters. + * such as EAL. Not standard or organic, because at the moment it's undesireable to completely mute characters. - - Can now actually cause muting, if destroyed / removed. */ /obj/item/organ/internal/voicebox @@ -10,6 +10,7 @@ parent_organ = BP_TORSO // We don't have a neck area organ_tag = O_VOICE will_assist_languages = list(LANGUAGE_GALCOM) + var/mute = FALSE /obj/item/organ/internal/voicebox/New() ..() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index c14fac25ba..215ef65ced 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -36,7 +36,10 @@ var/list/organ_cache = list() var/list/will_assist_languages = list() var/list/datum/language/assists_languages = list() - var/list/organ_verbs // Verbs added by the organ when present in the body. + // Organ verb vars. + var/list/organ_verbs // Verbs added by the organ when present in the body. + var/list/target_parent_classes = list() // Is the parent supposed to be organic, robotic, assisted? + var/forgiving_class = TRUE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. /obj/item/organ/Destroy() @@ -75,6 +78,7 @@ var/list/organ_cache = list() if(E.internal_organs == null) E.internal_organs = list() E.internal_organs |= src + H.internal_organs_by_name[organ_tag] = src if(dna) if(!blood_DNA) blood_DNA = list() @@ -289,7 +293,7 @@ var/list/organ_cache = list() W.time_inflicted = world.time //Note: external organs have their own version of this proc -/obj/item/organ/proc/take_damage(amount, var/silent=0) +/obj/item/organ/take_damage(amount, var/silent=0) if(src.robotic >= ORGAN_ROBOT) src.damage = between(0, src.damage + (amount * 0.8), max_damage) else @@ -441,12 +445,12 @@ var/list/organ_cache = list() all_organs |= owner.internal_organs for(var/obj/item/organ/O in all_organs) - if(!(O.status & ORGAN_DEAD) && O.organ_verbs) + if(!(O.status & ORGAN_DEAD) && O.organ_verbs && O.check_verb_compatability()) for(var/verb_type in O.organ_verbs) if(verb_type in organ_verbs) save_verbs |= verb_type - if(!removed && organ_verbs) + if(!removed && organ_verbs && check_verb_compatability()) for(var/verb_path in organ_verbs) owner.verbs |= verb_path else if(organ_verbs) @@ -457,3 +461,32 @@ var/list/organ_cache = list() /obj/item/organ/proc/handle_organ_proc_special() // Called when processed. return + +/obj/item/organ/proc/check_verb_compatability() // Used for determining if an organ should give or remove its verbs. I.E., FBP part in a human, no verbs. If true, keep or add. + if(owner) + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + var/obj/item/organ/O = H.get_organ(parent_organ) + if(forgiving_class) + if(O.robotic <= ORGAN_ASSISTED && robotic <= ORGAN_LIFELIKE) // Parent is organic or assisted, we are at most synthetic. + return TRUE + + if(O.robotic >= ORGAN_ROBOT && robotic >= ORGAN_ASSISTED) // Parent is synthetic, and we are biosynthetic at least. + return TRUE + + if(!target_parent_classes || !target_parent_classes.len) // Default checks, if we're not looking for a Specific type. + + if(O.robotic == robotic) // Same thing, we're fine. + return TRUE + + if(O.robotic < ORGAN_ROBOT && robotic < ORGAN_ROBOT) + return TRUE + + if(O.robotic > ORGAN_ASSISTED && robotic > ORGAN_ASSISTED) + return TRUE + + else + if(O.robotic in target_parent_classes) + return TRUE + + return FALSE diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 048a91271b..c8b02f02b8 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -952,6 +952,13 @@ Note that amputating the affected organ does in fact remove the infection from t qdel(src) + if(victim.l_hand) + if(istype(victim.l_hand,/obj/item/weapon/material/twohanded)) //if they're holding a two-handed weapon, drop it now they've lost a hand + victim.l_hand.update_held_icon() + if(victim.r_hand) + if(istype(victim.r_hand,/obj/item/weapon/material/twohanded)) + victim.r_hand.update_held_icon() + /**************************************************** HELPERS ****************************************************/ diff --git a/code/modules/organs/organ_external_vr.dm b/code/modules/organs/organ_external_vr.dm index 0e3fb07d9e..491cce9428 100644 --- a/code/modules/organs/organ_external_vr.dm +++ b/code/modules/organs/organ_external_vr.dm @@ -13,3 +13,7 @@ min_broken_damage = o_min_broken_damage else return ..() + +/obj/item/organ/external/proc/is_hidden_by_tail() + if(owner && owner.tail_style && owner.tail_style.hide_body_parts && (organ_tag in owner.tail_style.hide_body_parts)) + return 1 \ No newline at end of file diff --git a/code/modules/organs/robolimbs.dm b/code/modules/organs/robolimbs.dm index fd59a97d07..8f0ef8174c 100644 --- a/code/modules/organs/robolimbs.dm +++ b/code/modules/organs/robolimbs.dm @@ -129,7 +129,9 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ icon = 'icons/mob/human_races/cyberlimbs/grayson/grayson_main.dmi' unavailable_to_build = 1 monitor_styles = "blank=grayson_off;\ + red=grayson_red;\ green=grayson_green;\ + blue=grayson_blue;\ rgb=grayson_rgb" /datum/robolimb/grayson_alt1 @@ -177,7 +179,8 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "This rather thick limb has a militaristic green plating." icon = 'icons/mob/human_races/cyberlimbs/hephaestus/hephaestus_alt2.dmi' unavailable_to_build = 1 - + monitor_styles = "red=athena_red;\ + blank=athena_off" /datum/robolimb/hephaestus_monitor company = "Hephaestus Monitor" @@ -266,7 +269,9 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ icon = 'icons/mob/human_races/cyberlimbs/xion/xion_alt2.dmi' unavailable_to_build = 1 monitor_styles = "blank=xion_off;\ + red=xion_red;\ green=xion_green;\ + blue=xion_blue;\ rgb=xion_rgb" /datum/robolimb/xion_alt3 diff --git a/code/modules/organs/subtypes/slime.dm b/code/modules/organs/subtypes/slime.dm index f22b90caea..5fb54a21b4 100644 --- a/code/modules/organs/subtypes/slime.dm +++ b/code/modules/organs/subtypes/slime.dm @@ -56,3 +56,89 @@ max_damage = 30 encased = 0 spread_dam = 1 + +/* + * Internal Slime organs. + */ + +/obj/item/organ/internal/heart/grey/colormatch/slime + name = "pneumatic network" + desc = "A disgusting sac of goo." + icon_state = "sac_slime" + dead_icon = null + standard_pulse_level = PULSE_NONE + +/obj/item/organ/internal/heart/grey/colormatch/slime/process() + ..() + if(!(QDELETED(src)) && src.loc != owner) + visible_message("\The [src] splatters!") + var/turf/T = get_turf(src) + var/obj/effect/decal/cleanable/blood/B = new (T) + + B.basecolor = src.color + B.update_icon() + qdel(src) + +/obj/item/organ/internal/regennetwork + name = "pneumoregenesis network" + parent_organ = BP_TORSO + organ_tag = O_REGBRUTE + + icon_state = "sac_slime" + + var/strain = 0 // The amount of stress this organ is under. Capped at min_broken_damage, usually half its max damage. + + var/last_strain_increase = 0 // World time of the last increase in strain. + var/strain_regen_cooldown = 5 MINUTES + +/obj/item/organ/internal/regennetwork/Initialize() + ..() + var/mob/living/carbon/human/H = null + spawn(15) + if(ishuman(owner)) + H = owner + color = H.species.get_blood_colour(H) + +/obj/item/organ/internal/regennetwork/proc/get_strain_percent(var/cost) + adjust_strain(cost) + + if((status & ORGAN_CUT_AWAY) || (status & ORGAN_BROKEN) || (status & ORGAN_DEAD)) + return 1 + + return round((strain / min_broken_damage) * 10) / 10 + +/obj/item/organ/internal/regennetwork/proc/adjust_strain(var/amount) + if(amount < 0 && world.time < (last_strain_increase + strain_regen_cooldown)) + return + + else if(amount > 0) + last_strain_increase = world.time + + strain = CLAMP(strain + amount, 0, min_broken_damage) + +/obj/item/organ/internal/regennetwork/process() + ..() + + if(!(QDELETED(src)) && src.loc != owner) + visible_message("\The [src] splatters!") + var/turf/T = get_turf(src) + var/obj/effect/decal/cleanable/blood/B = new (T) + + B.basecolor = src.color + B.update_icon() + qdel(src) + + if(src && !is_bruised()) + adjust_strain(-0.25 * max(0, (min_broken_damage - damage) / min_broken_damage)) // Decrease the current strain with respect to the current strain level. + +/obj/item/organ/internal/regennetwork/burn + name = "thermoregenesis network" + organ_tag = O_REGBURN + +/obj/item/organ/internal/regennetwork/oxy + name = "respiroregenesis network" + organ_tag = O_REGOXY + +/obj/item/organ/internal/regennetwork/tox + name = "toxoregenesis network" + organ_tag = O_REGTOX diff --git a/code/modules/organs/subtypes/standard_vr.dm b/code/modules/organs/subtypes/standard_vr.dm index 1ad51c804c..afcb8825ad 100644 --- a/code/modules/organs/subtypes/standard_vr.dm +++ b/code/modules/organs/subtypes/standard_vr.dm @@ -1,14 +1,77 @@ -/obj/item/organ/external/head/blankeyes - eye_icon_location = 'icons/mob/human_face_vr.dmi' - eye_icon = "blank_eyes" +//For custom heads with custom parts since the base code is restricted to a single icon file. + +/obj/item/organ/external/head/vr/get_icon() + + ..() + overlays.Cut() + if(!owner || !owner.species) + return + + for(var/M in markings) + var/datum/sprite_accessory/marking/mark_style = markings[M]["datum"] + var/icon/mark_s = new/icon("icon" = mark_style.icon, "icon_state" = "[mark_style.icon_state]-[organ_tag]") + mark_s.Blend(markings[M]["color"], mark_style.color_blend_mode) + overlays |= mark_s //So when it's not on your body, it has icons + mob_icon.Blend(mark_s, ICON_OVERLAY) //So when it's on your body, it has icons + icon_cache_key += "[M][markings[M]["color"]]" + + if(owner.should_have_organ(O_EYES))//Moved on top of markings. + var/obj/item/organ/internal/eyes/eyes = owner.internal_organs_by_name[O_EYES] + if(eye_icon) + var/icon/eyes_icon = new/icon(eye_icons_vr, eye_icon_vr) + if(eyes) + eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD) + else + eyes_icon.Blend(rgb(128,0,0), ICON_ADD) + mob_icon.Blend(eyes_icon, ICON_OVERLAY) + overlays |= eyes_icon + + if(owner.lip_style && (species && (species.appearance_flags & HAS_LIPS))) + var/icon/lip_icon = new/icon('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s") + overlays |= lip_icon + mob_icon.Blend(lip_icon, ICON_OVERLAY) + + if(owner.f_style) + var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[owner.f_style] + if(facial_hair_style && facial_hair_style.species_allowed && (species.get_bodytype(owner) in facial_hair_style.species_allowed)) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(facial_hair_style.do_colouration) + facial_s.Blend(rgb(owner.r_facial, owner.g_facial, owner.b_facial), ICON_ADD) + overlays |= image(facial_s, "pixel_y" = head_offset) + + if(owner.h_style && !(owner.head && (owner.head.flags_inv & BLOCKHEADHAIR))) + var/datum/sprite_accessory/hair_style = hair_styles_list[owner.h_style] + if(hair_style && (species.get_bodytype(owner) in hair_style.species_allowed)) + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + if(hair_style.do_colouration && islist(h_col) && h_col.len >= 3) + hair_s.Blend(rgb(h_col[1], h_col[2], h_col[3]), ICON_MULTIPLY) + overlays |= image(hair_s, "pixel_y" = head_offset) /obj/item/organ/external/head/sergal eye_icon_location = 'icons/mob/human_face_vr.dmi' eye_icon = "eyes_sergal" +/obj/item/organ/external/head/vr + var/eye_icons_vr = 'icons/mob/human_face_vr.dmi' + var/eye_icon_vr = "blank_eyes" + var/head_offset = 0 + eye_icon = "blank_eyes" + +/obj/item/organ/external/head/vr/sergal + eye_icon_vr = "eyes_sergal" + +/obj/item/organ/external/head/vr/werebeast + eye_icons_vr = 'icons/mob/werebeast_face_vr.dmi' + eye_icon_vr = "werebeast_eyes" + head_offset = 6 + +/obj/item/organ/external/head/blankeyes + eye_icon_location = 'icons/mob/human_face_vr.dmi' + eye_icon = "blank_eyes" + /obj/item/organ/external/head/shadekin cannot_gib = 1 cannot_amputate = 1 eye_icon_location = 'icons/mob/human_face_vr.dmi' - eye_icon = "eyes_shadekin_blue" \ No newline at end of file + eye_icon = "eyes_shadekin_blue" diff --git a/code/modules/organs/subtypes/unathi.dm b/code/modules/organs/subtypes/unathi.dm index 212330577c..03ff671b63 100644 --- a/code/modules/organs/subtypes/unathi.dm +++ b/code/modules/organs/subtypes/unathi.dm @@ -47,4 +47,11 @@ owner.adjustToxLoss(0.3 * PROCESS_ACCURACY) /obj/item/organ/internal/brain/unathi - color = "#b3cbc3" \ No newline at end of file + color = "#b3cbc3" + +/obj/item/organ/internal/stomach/unathi + color = "#b3cbc3" + max_acid_volume = 40 + +/obj/item/organ/internal/intestine/unathi + color = "#b3cbc3" diff --git a/code/modules/planet/sif.dm b/code/modules/planet/sif.dm index 3ee2fdd976..921142812d 100644 --- a/code/modules/planet/sif.dm +++ b/code/modules/planet/sif.dm @@ -197,6 +197,8 @@ var/datum/planet/sif/planet_sif = null icon_state = "snowfall_med" temp_high = T0C // 0c temp_low = 243.15 // -30c + wind_high = 2 + wind_low = 0 light_modifier = 0.5 flight_failure_modifier = 5 transition_chances = list( @@ -229,6 +231,8 @@ var/datum/planet/sif/planet_sif = null icon_state = "snowfall_heavy" temp_high = 243.15 // -30c temp_low = 233.15 // -40c + wind_high = 4 + wind_low = 2 light_modifier = 0.3 flight_failure_modifier = 10 transition_chances = list( @@ -258,6 +262,8 @@ var/datum/planet/sif/planet_sif = null /datum/weather/sif/rain name = "rain" icon_state = "rain" + wind_high = 2 + wind_low = 1 light_modifier = 0.5 effect_message = "Rain falls on you." @@ -306,6 +312,8 @@ var/datum/planet/sif/planet_sif = null icon_state = "storm" temp_high = 243.15 // -30c temp_low = 233.15 // -40c + wind_high = 4 + wind_low = 2 light_modifier = 0.3 flight_failure_modifier = 10 effect_message = "Rain falls on you, drenching you in water." @@ -337,24 +345,6 @@ var/datum/planet/sif/planet_sif = null var/turf/T = get_turf(L) if(!T.outdoors) continue // They're indoors, so no need to rain on them. - - // Lazy wind code - if(prob(10)) - if(istype(L.get_active_hand(), /obj/item/weapon/melee/umbrella)) - var/obj/item/weapon/melee/umbrella/U = L.get_active_hand() - if(U.open) - to_chat(L, "You struggle to keep hold of your umbrella!") - L.Stun(20) // This is not nearly as long as it seems - playsound(L, 'sound/effects/rustle1.ogg', 100, 1) // Closest sound I've got to "Umbrella in the wind" - else if(istype(L.get_inactive_hand(), /obj/item/weapon/melee/umbrella)) - var/obj/item/weapon/melee/umbrella/U = L.get_inactive_hand() - if(U.open) - to_chat(L, "A gust of wind yanks the umbrella from your hand!") - playsound(L, 'sound/effects/rustle1.ogg', 100, 1) - L.drop_from_inventory(U) - U.toggle_umbrella() - U.throw_at(get_edge_target_turf(U, pick(alldirs)), 8, 1, L) - // If they have an open umbrella, it'll guard from rain if(istype(L.get_active_hand(), /obj/item/weapon/melee/umbrella)) var/obj/item/weapon/melee/umbrella/U = L.get_active_hand() @@ -492,6 +482,8 @@ var/datum/planet/sif/planet_sif = null light_color = "#FF0000" temp_high = 323.15 // 50c temp_low = 313.15 // 40c + wind_high = 6 + wind_low = 3 flight_failure_modifier = 50 transition_chances = list( WEATHER_ASH_STORM = 100 @@ -563,4 +555,4 @@ var/datum/planet/sif/planet_sif = null if(!istype(T)) return if(T.outdoors) - radiation_repository.radiate(T, rand(fallout_rad_low, fallout_rad_high)) + SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high)) diff --git a/maps/tether/tether_virgo3b.dm b/code/modules/planet/virgo3b_vr.dm similarity index 92% rename from maps/tether/tether_virgo3b.dm rename to code/modules/planet/virgo3b_vr.dm index d7208204df..791841aca7 100644 --- a/maps/tether/tether_virgo3b.dm +++ b/code/modules/planet/virgo3b_vr.dm @@ -9,14 +9,7 @@ var/datum/planet/virgo3b/planet_virgo3b = null amounts of both oxygen and nitrogen. Fortunately, the oxygen is not enough to be combustible in any meaningful way, however \ the phoron is desirable by many corporations, including NanoTrasen." current_time = new /datum/time/virgo3b() - expected_z_levels = list( - Z_LEVEL_SURFACE_LOW, - Z_LEVEL_SURFACE_MID, - Z_LEVEL_SURFACE_HIGH, - Z_LEVEL_SURFACE_MINE, - Z_LEVEL_SOLARS, - Z_LEVEL_PLAINS - ) +// expected_z_levels = list(1) // This is defined elsewhere. planetary_wall_type = /turf/unsimulated/wall/planetary/virgo3b /datum/planet/virgo3b/New() @@ -171,8 +164,8 @@ var/datum/planet/virgo3b/planet_virgo3b = null /datum/weather/virgo3b/light_snow name = "light snow" icon_state = "snowfall_light" - temp_high = 235 - temp_low = 225 + temp_high = 235.15 + temp_low = 225.15 light_modifier = 0.7 transition_chances = list( WEATHER_OVERCAST = 20, @@ -189,8 +182,10 @@ var/datum/planet/virgo3b/planet_virgo3b = null /datum/weather/virgo3b/snow name = "moderate snow" icon_state = "snowfall_med" - temp_high = 230 - temp_low = 220 + temp_high = 230.15 + temp_low = 220.15 + wind_high = 2 + wind_low = 0 light_modifier = 0.5 flight_failure_modifier = 5 transition_chances = list( @@ -221,8 +216,10 @@ var/datum/planet/virgo3b/planet_virgo3b = null /datum/weather/virgo3b/blizzard name = "blizzard" icon_state = "snowfall_heavy" - temp_high = 215 - temp_low = 200 + temp_high = 215.15 + temp_low = 200.15 + wind_high = 4 + wind_low = 2 light_modifier = 0.3 flight_failure_modifier = 10 transition_chances = list( @@ -252,6 +249,8 @@ var/datum/planet/virgo3b/planet_virgo3b = null /datum/weather/virgo3b/rain name = "rain" icon_state = "rain" + wind_high = 2 + wind_low = 1 light_modifier = 0.5 effect_message = "Rain falls on you." @@ -296,6 +295,8 @@ var/datum/planet/virgo3b/planet_virgo3b = null /datum/weather/virgo3b/storm name = "storm" icon_state = "storm" + wind_high = 4 + wind_low = 2 light_modifier = 0.3 flight_failure_modifier = 10 effect_message = "Rain falls on you, drenching you in water." @@ -326,23 +327,6 @@ var/datum/planet/virgo3b/planet_virgo3b = null if(!T.outdoors) continue // They're indoors, so no need to rain on them. - // Lazy wind code - if(prob(10)) - if(istype(L.get_active_hand(), /obj/item/weapon/melee/umbrella)) - var/obj/item/weapon/melee/umbrella/U = L.get_active_hand() - if(U.open) - to_chat(L, "You struggle to keep hold of your umbrella!") - L.Stun(20) // This is not nearly as long as it seems - playsound(L, 'sound/effects/rustle1.ogg', 100, 1) // Closest sound I've got to "Umbrella in the wind" - else if(istype(L.get_inactive_hand(), /obj/item/weapon/melee/umbrella)) - var/obj/item/weapon/melee/umbrella/U = L.get_inactive_hand() - if(U.open) - to_chat(L, "A gust of wind yanks the umbrella from your hand!") - playsound(L, 'sound/effects/rustle1.ogg', 100, 1) - L.drop_from_inventory(U) - U.toggle_umbrella() - U.throw_at(get_edge_target_turf(U, pick(alldirs)), 8, 1, L) - // If they have an open umbrella, it'll guard from rain if(istype(L.get_active_hand(), /obj/item/weapon/melee/umbrella)) var/obj/item/weapon/melee/umbrella/U = L.get_active_hand() @@ -474,6 +458,8 @@ var/datum/planet/virgo3b/planet_virgo3b = null light_color = "#FF0000" temp_high = 323.15 // 50c temp_low = 313.15 // 40c + wind_high = 6 + wind_low = 3 flight_failure_modifier = 50 transition_chances = list( WEATHER_ASH_STORM = 100 @@ -545,5 +531,5 @@ var/datum/planet/virgo3b/planet_virgo3b = null if(!istype(T)) return if(T.outdoors) - radiation_repository.radiate(T, rand(fallout_rad_low, fallout_rad_high)) + SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high)) diff --git a/code/modules/planet/weather.dm b/code/modules/planet/weather.dm index 6e4447f4e9..c5202ab067 100644 --- a/code/modules/planet/weather.dm +++ b/code/modules/planet/weather.dm @@ -2,8 +2,8 @@ var/datum/planet/our_planet = null // Reference to the planet datum that holds this datum. var/datum/weather/current_weather = null // The current weather that is affecting the planet. var/temperature = T20C // The temperature to set planetary walls to. - var/wind_dir = 0 // Not implemented. - var/wind_speed = 0 // Not implemented. + var/wind_dir = 0 // The direction the wind is blowing. Moving against the wind slows you down, while moving with it speeds you up. + var/wind_speed = 0 // How fast or slow a mob can be due to wind acting on them. var/list/allowed_weather_types = list() // Assoc list of weather identifiers, containing the actual weather datum. var/list/roundstart_weather_chances = list() // Assoc list of weather identifiers and their odds of being picked to happen at roundstart. var/next_weather_shift = null // world.time when the weather subsystem will advance the forecast. @@ -42,6 +42,7 @@ update_icon_effects() update_temperature() + update_wind() if(old_light_modifier && current_weather.light_modifier != old_light_modifier) // Updating the sun should be done sparingly. our_planet.update_sun() log_debug("[our_planet.name]'s weather is now [new_weather], with a temperature of [temperature]°K ([temperature - T0C]°C | [temperature * 1.8 - 459.67]°F).") @@ -107,6 +108,25 @@ temperature = LERP(current_weather.temp_low, current_weather.temp_high, our_planet.sun_position) our_planet.needs_work |= PLANET_PROCESS_TEMP +/datum/weather_holder/proc/update_wind() + var/new_wind_speed = rand(current_weather.wind_low, current_weather.wind_high) + if(!new_wind_speed) + wind_speed = 0 + wind_dir = 0 + return + wind_speed = new_wind_speed + wind_dir = pick(alldirs) + var/message = "You feel the wind blowing [wind_speed > 2 ? "strongly ": ""]towards the [dir2text(wind_dir)]." + message_all_outdoor_players(span("warning", message)) + +/datum/weather_holder/proc/message_all_outdoor_players(message) + for(var/mob/M in player_list) // Don't need to care about clientless mobs. + if(M.z in our_planet.expected_z_levels) + var/turf/T = get_turf(M) + if(!T.outdoors) + continue + to_chat(M, message) + /datum/weather_holder/proc/get_weather_datum(desired_type) return allowed_weather_types[desired_type] @@ -116,12 +136,7 @@ return var/message = pick(current_weather.transition_messages) // So everyone gets the same message. - for(var/mob/M in player_list) // Don't need to care about clientless mobs. - if(M.z in our_planet.expected_z_levels) - var/turf/T = get_turf(M) - if(!T.outdoors) - continue - to_chat(M, message) + message_all_outdoor_players(message) /datum/weather var/name = "weather base" @@ -129,6 +144,8 @@ var/icon_state = null // Icon to apply to turf undergoing weather. var/temp_high = T20C // Temperature to apply when at noon. var/temp_low = T0C // Temperature to apply when at midnight. + var/wind_high = 0 // Upper bound for mob slowdown when walking against the wind, and speedup when walking with it. Randomized between this and wind_low. + var/wind_low = 0 // Lower bound for above. var/light_modifier = 1.0 // Lower numbers means more darkness. var/light_color = null // If set, changes how the day/night light looks. var/flight_failure_modifier = 0 // Some types of weather make flying harder, and therefore make crashes more likely. (This is not implemented) diff --git a/code/modules/power/apc_vr.dm b/code/modules/power/apc_vr.dm new file mode 100644 index 0000000000..c08c9b8db0 --- /dev/null +++ b/code/modules/power/apc_vr.dm @@ -0,0 +1,9 @@ +/obj/machinery/power/apc/proc/update_area() + var/area/NA = get_area(src) + if(!(NA == area)) + if(area.apc == src) + area.apc = null + NA.apc = src + area = NA + name = "[area.name] APC" + update() \ No newline at end of file diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm index f0eca3add6..5444c46b03 100644 --- a/code/modules/power/fusion/core/core_field.dm +++ b/code/modules/power/fusion/core/core_field.dm @@ -313,7 +313,7 @@ radiation += plasma_temperature/2 plasma_temperature = 0 - radiation_repository.radiate(src, radiation) + SSradiation.radiate(src, radiation) Radiate() /obj/effect/fusion_em_field/proc/Radiate() @@ -522,7 +522,7 @@ //Reaction radiation is fairly buggy and there's at least three procs dealing with radiation here, this is to ensure constant radiation output. /obj/effect/fusion_em_field/proc/radiation_scale() - radiation_repository.radiate(src, 2 + plasma_temperature / PLASMA_TEMP_RADIATION_DIVISIOR) + SSradiation.radiate(src, 2 + plasma_temperature / PLASMA_TEMP_RADIATION_DIVISIOR) //Somehow fixing the radiation issue managed to break this, but moving it to it's own proc seemed to have fixed it. I don't know. /obj/effect/fusion_em_field/proc/temp_dump() diff --git a/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm b/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm index 4149274351..73543ead92 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm @@ -46,7 +46,7 @@ return PROCESS_KILL if(istype(loc, /turf)) - radiation_repository.radiate(src, max(1,CEILING(radioactivity/30, 1))) + SSradiation.radiate(src, max(1,CEILING(radioactivity/30, 1))) /obj/item/weapon/fuel_assembly/Destroy() STOP_PROCESSING(SSobj, src) diff --git a/code/modules/power/fusion/fusion_reactions.dm b/code/modules/power/fusion/fusion_reactions.dm index 623f70bd6b..88117f8164 100644 --- a/code/modules/power/fusion/fusion_reactions.dm +++ b/code/modules/power/fusion/fusion_reactions.dm @@ -120,7 +120,7 @@ proc/get_fusion_reaction(var/p_react, var/s_react, var/m_energy) var/radiation_level = 200 // Copied from the SM for proof of concept. //Not any more --Cirra //Use the whole z proc --Leshana - radiation_repository.z_radiate(locate(1, 1, holder.z), radiation_level, 1) + SSradiation.z_radiate(locate(1, 1, holder.z), radiation_level, 1) for(var/mob/living/mob in living_mob_list) var/turf/T = get_turf(mob) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 24132d2988..ba60c84d32 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -9,6 +9,14 @@ #define LIGHT_BROKEN 2 #define LIGHT_BURNED 3 #define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb +#define LIGHTING_POWER_FACTOR 2 //5W per luminosity * range //VOREStation Edit: why the fuck are lights eating so much power, 2W per thing + +var/global/list/light_type_cache = list() +/proc/get_light_type_instance(var/light_type) + . = light_type_cache[light_type] + if(!.) + . = new light_type + light_type_cache[light_type] = . /obj/machinery/light_construct name = "light fixture frame" @@ -19,16 +27,28 @@ plane = MOB_PLANE layer = ABOVE_MOB_LAYER var/stage = 1 - var/fixture_type = "tube" + var/fixture_type = /obj/machinery/light var/sheets_refunded = 2 - var/obj/machinery/light/newlight = null -/obj/machinery/light_construct/New() - ..() - if (fixture_type == "bulb") - icon_state = "bulb-construct-stage1" - if (fixture_type == "flamp") - icon_state = "flamp-construct-stage1" +/obj/machinery/light_construct/New(var/atom/newloc, var/newdir, var/building = 0, var/datum/frame/frame_types/frame_type, var/obj/machinery/light/fixture = null) + ..(newloc) + if(fixture) + fixture_type = fixture.type + fixture.transfer_fingerprints_to(src) + set_dir(fixture.dir) + stage = 2 + else if(newdir) + set_dir(newdir) + update_icon() + +/obj/machinery/light_construct/update_icon() + switch(stage) + if(1) + icon_state = "tube-construct-stage1" + if(2) + icon_state = "tube-construct-stage2" + if(3) + icon_state = "tube-empty" /obj/machinery/light_construct/examine(mob/user) if(!..(user, 2)) @@ -36,21 +56,18 @@ switch(src.stage) if(1) - user << "It's an empty frame." - return + to_chat(user, "It's an empty frame.") if(2) - user << "It's wired." - return + to_chat(user, "It's wired.") if(3) - user << "The casing is closed." - return + to_chat(user, "The casing is closed.") /obj/machinery/light_construct/attackby(obj/item/weapon/W as obj, mob/user as mob) src.add_fingerprint(user) if (W.is_wrench()) if (src.stage == 1) playsound(src, W.usesound, 75, 1) - usr << "You begin deconstructing [src]." + to_chat(usr, "You begin deconstructing [src].") if (!do_after(usr, 30 * W.toolspeed)) return new /obj/item/stack/material/steel( get_turf(src.loc), sheets_refunded ) @@ -59,23 +76,17 @@ playsound(src.loc, 'sound/items/Deconstruct.ogg', 75, 1) qdel(src) if (src.stage == 2) - usr << "You have to remove the wires first." + to_chat(usr, "You have to remove the wires first.") return if (src.stage == 3) - usr << "You have to unscrew the case first." + to_chat(usr, "You have to unscrew the case first.") return if(W.is_wirecutter()) if (src.stage != 2) return src.stage = 1 - switch(fixture_type) - if ("tube") - src.icon_state = "tube-construct-stage1" - if("bulb") - src.icon_state = "bulb-construct-stage1" - if("flamp") - src.icon_state = "flamp-construct-stage1" + src.update_icon() new /obj/item/stack/cable_coil(get_turf(src.loc), 1, "red") user.visible_message("[user.name] removes the wiring from [src].", \ "You remove the wiring from [src].", "You hear a noise.") @@ -86,42 +97,22 @@ if (src.stage != 1) return var/obj/item/stack/cable_coil/coil = W if (coil.use(1)) - switch(fixture_type) - if ("tube") - src.icon_state = "tube-construct-stage2" - if("bulb") - src.icon_state = "bulb-construct-stage2" - if("flamp") - src.icon_state = "flamp-construct-stage2" src.stage = 2 + src.update_icon() user.visible_message("[user.name] adds wires to [src].", \ "You add wires to [src].") return if(W.is_screwdriver()) if (src.stage == 2) - switch(fixture_type) - if ("tube") - src.icon_state = "tube-empty" - if("bulb") - src.icon_state = "bulb-empty" - if("flamp") - src.icon_state = "flamp-empty" src.stage = 3 + src.update_icon() user.visible_message("[user.name] closes [src]'s casing.", \ "You close [src]'s casing.", "You hear a noise.") playsound(src, W.usesound, 75, 1) - switch(fixture_type) - - if("tube") - newlight = new /obj/machinery/light/built(src.loc) - if ("bulb") - newlight = new /obj/machinery/light/small/built(src.loc) - if ("flamp") - newlight = new /obj/machinery/light/flamp/built(src.loc) - - newlight.dir = src.dir + var/obj/machinery/light/newlight = new fixture_type(src.loc, src) + newlight.set_dir(src.dir) src.transfer_fingerprints_to(newlight) qdel(src) return @@ -134,9 +125,18 @@ icon_state = "bulb-construct-stage1" anchored = 1 stage = 1 - fixture_type = "bulb" + fixture_type = /obj/machinery/light/small sheets_refunded = 1 +/obj/machinery/light_construct/small/update_icon() + switch(stage) + if(1) + icon_state = "bulb-construct-stage1" + if(2) + icon_state = "bulb-construct-stage2" + if(3) + icon_state = "bulb-empty" + /obj/machinery/light_construct/flamp name = "floor light fixture frame" desc = "A floor light fixture under construction." @@ -146,13 +146,22 @@ plane = OBJ_PLANE layer = OBJ_LAYER stage = 1 - fixture_type = "flamp" + fixture_type = /obj/machinery/light/flamp sheets_refunded = 2 +/obj/machinery/light_construct/flamp/update_icon() + switch(stage) + if(1) + icon_state = "flamp-construct-stage1" + if(2) + icon_state = "flamp-construct-stage2" + if(3) + icon_state = "flamp-empty" + // the standard tube light fixture /obj/machinery/light name = "light fixture" - icon = 'icons/obj/lighting_vr.dmi' + icon = 'icons/obj/lighting_vr.dmi' //VOREStation Edit var/base_state = "tube" // base description and icon_state icon_state = "tube1" desc = "A lighting fixture." @@ -161,23 +170,25 @@ layer = ABOVE_MOB_LAYER use_power = 2 idle_power_usage = 2 - active_power_usage = 20 // VOREStation Edit - Keep lights at 20 power + active_power_usage = 10 power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list var/on = 0 // 1 if on, 0 if off - var/brightness_range = 10 // luminosity when on, also used in power calculation //VOREStation Edit - 8->10 - var/brightness_power = 0.8 - var/brightness_color = LIGHT_COLOR_FLUORESCENT_TUBE //VOREStation Edit - Our tubes are whiter + var/brightness_range + var/brightness_power + var/brightness_color var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN var/flickering = 0 var/light_type = /obj/item/weapon/light/tube // the type of light item - var/fitting = "tube" + var/construct_type = /obj/machinery/light_construct var/switchcount = 0 // count of number of times switched on/off // this is used to calc the probability the light burns out var/rigged = 0 // true if rigged to explode + //VOREStation Edit Start var/needsound = FALSE // Flag to prevent playing turn-on sound multiple times, and from playing at roundstart var/shows_alerts = TRUE // Flag for if this fixture should show alerts. Make sure icon states exist! var/current_alert = null // Which alert are we showing right now? + //VOREStation Edit End var/auto_flicker = FALSE // If true, will constantly flicker, so long as someone is around to see it (otherwise its a waste of CPU). @@ -189,91 +200,74 @@ /obj/machinery/light/small icon_state = "bulb1" base_state = "bulb" - fitting = "bulb" - brightness_range = 5 //VOREStation Edit - 4->5 - brightness_color = LIGHT_COLOR_INCANDESCENT_BULB desc = "A small lighting fixture." light_type = /obj/item/weapon/light/bulb - shows_alerts = FALSE + construct_type = /obj/machinery/light_construct/small + shows_alerts = FALSE //VOREStation Edit /obj/machinery/light/small/flicker auto_flicker = TRUE /obj/machinery/light/flamp - icon = 'icons/obj/lighting.dmi' + icon = 'icons/obj/lighting.dmi' //VOREStation Edit icon_state = "flamp1" base_state = "flamp" - fitting = "bulb" - brightness_range = 8 //VOREStation Edit - 4->8 plane = OBJ_PLANE layer = OBJ_LAYER - brightness_color = LIGHT_COLOR_INCANDESCENT_BULB desc = "A floor lamp." light_type = /obj/item/weapon/light/bulb - shows_alerts = FALSE + construct_type = /obj/machinery/light_construct/flamp + shows_alerts = FALSE //VOREStation Edit var/lamp_shade = 1 +/obj/machinery/light/flamp/New(atom/newloc, obj/machinery/light_construct/construct = null) + ..(newloc, construct) + + if(construct) + lamp_shade = 0 + update_icon() + /obj/machinery/light/flamp/flicker auto_flicker = TRUE - /obj/machinery/light/small/emergency - brightness_range = 4 - brightness_color = "#da0205" + light_type = /obj/item/weapon/light/bulb/red /obj/machinery/light/small/emergency/flicker auto_flicker = TRUE - /obj/machinery/light/spot name = "spotlight" - fitting = "large tube" light_type = /obj/item/weapon/light/tube/large - shows_alerts = FALSE - brightness_range = 12 - brightness_power = 0.9 + shows_alerts = FALSE //VOREStation Edit /obj/machinery/light/spot/flicker auto_flicker = TRUE - -/obj/machinery/light/built/New() - status = LIGHT_EMPTY - update(0) - ..() - -/obj/machinery/light/small/built/New() - status = LIGHT_EMPTY - update(0) - ..() - -/obj/machinery/light/flamp/built/New() - status = LIGHT_EMPTY - lamp_shade = 0 - update(0) - ..() //VOREStation Add - Shadeless! /obj/machinery/light/flamp/noshade/New() lamp_shade = 0 update(0) ..() //VOREStation Add End + // create a new lighting fixture -/obj/machinery/light/New() - ..() +/obj/machinery/light/New(atom/newloc, obj/machinery/light_construct/construct = null) + ..(newloc) - spawn(2) - on = has_power() + if(construct) + status = LIGHT_EMPTY + construct_type = construct.type + construct.transfer_fingerprints_to(src) + set_dir(construct.dir) + else + var/obj/item/weapon/light/L = get_light_type_instance(light_type) + update_from_bulb(L) + if(prob(L.broken_chance)) + broken(1) - switch(fitting) - if("tube") - if(prob(2)) - broken(1) - if("bulb") - if(prob(5)) - broken(1) - spawn(1) - update(0) + on = powered() + update(0) /obj/machinery/light/Destroy() var/area/A = get_area(src) @@ -286,10 +280,12 @@ switch(status) // set icon_states if(LIGHT_OK) + //VOREStation Edit Start if(shows_alerts && current_alert && on) icon_state = "[base_state]-alert-[current_alert]" else icon_state = "[base_state][on]" + //VOREStation Edit End if(LIGHT_EMPTY) icon_state = "[base_state]-empty" on = 0 @@ -320,7 +316,7 @@ else base_state = "flamp" ..() - +//VOREStation Edit Start /obj/machinery/light/proc/set_alert_atmos() if(shows_alerts) current_alert = "atmos" @@ -341,15 +337,17 @@ brightness_color = initial(brightness_color) || "" // Workaround for BYOND stupidity. Can't set it to null or it won't clear. if(on) update() - -// update the icon_state and luminosity of the light depending on its state +//VOREstation Edit End +// update lighting /obj/machinery/light/proc/update(var/trigger = 1) update_icon() + //VOREStation Edit Start if(!on) needsound = TRUE // Play sound next time we turn on else if(needsound) playsound(src.loc, 'sound/effects/lighton.ogg', 65, 1) needsound = FALSE // Don't play sound again until we've been turned off + //VOREStation Edit End if(on) if(light_range != brightness_range || light_power != brightness_power || light_color != brightness_color) @@ -375,14 +373,14 @@ use_power = 1 set_light(0) - active_power_usage = light_range * light_power + active_power_usage = ((light_range * light_power) * LIGHTING_POWER_FACTOR) /obj/machinery/light/attack_generic(var/mob/user, var/damage) if(!damage) return if(status == LIGHT_EMPTY||status == LIGHT_BROKEN) - user << "That object is useless to you." + to_chat(user, "That object is useless to you.") return if(!(status == LIGHT_OK||status == LIGHT_BURNED)) return @@ -391,6 +389,16 @@ broken() return 1 +/obj/machinery/light/take_damage(var/damage) + if(!damage) + return + if(status == LIGHT_EMPTY||status == LIGHT_BROKEN) + return + if(!(status == LIGHT_OK||status == LIGHT_BURNED)) + return + broken() + return 1 + /obj/machinery/light/blob_act() broken() @@ -402,20 +410,52 @@ // examine verb /obj/machinery/light/examine(mob/user) + var/fitting = get_fitting_name() switch(status) if(LIGHT_OK) - user << "[desc] It is turned [on? "on" : "off"]." + to_chat(user, "[desc] It is turned [on? "on" : "off"].") if(LIGHT_EMPTY) - user << "[desc] The [fitting] has been removed." + to_chat(user, "[desc] The [fitting] has been removed.") if(LIGHT_BURNED) - user << "[desc] The [fitting] is burnt out." + to_chat(user, "[desc] The [fitting] is burnt out.") if(LIGHT_BROKEN) - user << "[desc] The [fitting] has been smashed." + to_chat(user, "[desc] The [fitting] has been smashed.") +/obj/machinery/light/proc/get_fitting_name() + var/obj/item/weapon/light/L = light_type + return initial(L.name) +/obj/machinery/light/proc/update_from_bulb(obj/item/weapon/light/L) + status = L.status + switchcount = L.switchcount + rigged = L.rigged + brightness_range = L.brightness_range + brightness_power = L.brightness_power + brightness_color = L.brightness_color // attack with item - insert light (if right type), otherwise try to break the light +/obj/machinery/light/proc/insert_bulb(obj/item/weapon/light/L) + update_from_bulb(L) + qdel(L) + + on = powered() + update() + + if(on && rigged) + + log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") + + explode() + +/obj/machinery/light/proc/remove_bulb() + . = new light_type(src.loc, src) + + switchcount = 0 + status = LIGHT_EMPTY + update() + /obj/machinery/light/attackby(obj/item/W, mob/user) //Light replacer code @@ -429,34 +469,15 @@ // attempt to insert light if(istype(W, /obj/item/weapon/light)) if(status != LIGHT_EMPTY) - user << "There is a [fitting] already inserted." + to_chat(user, "There is a [get_fitting_name()] already inserted.") + return + if(!istype(W, light_type)) + to_chat(user, "This type of light requires a [get_fitting_name()].") return - else - src.add_fingerprint(user) - var/obj/item/weapon/light/L = W - if(istype(L, light_type)) - status = L.status - user << "You insert the [L.name]." - switchcount = L.switchcount - rigged = L.rigged - brightness_range = L.brightness_range - brightness_power = L.brightness_power - brightness_color = L.brightness_color - on = has_power() - update() - user.drop_item() //drop the item to update overlays and such - qdel(L) - - if(on && rigged) - - log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") - - explode() - else - user << "This type of light requires a [fitting]." - return + to_chat(user, "You insert [W].") + insert_bulb(W) + src.add_fingerprint(user) // attempt to break the light //If xenos decide they want to smash a light bulb with a toolbox, who am I to stop them? /N @@ -466,7 +487,7 @@ if(prob(1+W.force * 5)) - user << "You hit the light, and it smashes!" + to_chat(user, "You hit the light, and it smashes!") for(var/mob/M in viewers(src)) if(M == user) continue @@ -478,7 +499,7 @@ broken() else - user << "You hit the light!" + to_chat(user, "You hit the light!") // attempt to stick weapon into light socket else if(status == LIGHT_EMPTY) @@ -486,29 +507,11 @@ playsound(src, W.usesound, 75, 1) user.visible_message("[user.name] opens [src]'s casing.", \ "You open [src]'s casing.", "You hear a noise.") - var/obj/machinery/light_construct/newlight = null - switch(fitting) - if("tube") - newlight = new /obj/machinery/light_construct(src.loc) - newlight.icon_state = "tube-construct-stage2" - - if("bulb") - newlight = new /obj/machinery/light_construct/small(src.loc) - newlight.icon_state = "bulb-construct-stage2" - - if("flamp") - newlight = new /obj/machinery/light_construct/flamp(src.loc) - newlight.icon_state = "flamp-construct-stage2" - - newlight.dir = src.dir - newlight.stage = 2 - newlight.fingerprints = src.fingerprints - newlight.fingerprintshidden = src.fingerprintshidden - newlight.fingerprintslast = src.fingerprintslast + new construct_type(src.loc, fixture = src) qdel(src) return - user << "You stick \the [W] into the light socket!" + to_chat(user, "You stick \the [W] into the light socket!") if(has_power() && !(W.flags & NOCONDUCT)) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) @@ -521,7 +524,7 @@ if(W.is_wrench()) anchored = !anchored playsound(src, W.usesound, 50, 1) - user << "You [anchored ? "wrench" : "unwrench"] \the [src]." + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") if(!lamp_shade) if(istype(W, /obj/item/weapon/lampshade)) @@ -586,7 +589,7 @@ add_fingerprint(user) if(status == LIGHT_EMPTY) - user << "There is no [fitting] in this light." + to_chat(user, "There is no [get_fitting_name()] in this light.") return if(istype(user,/mob/living/carbon/human)) @@ -615,39 +618,22 @@ prot = 1 if(prot > 0 || (COLD_RESISTANCE in user.mutations)) - user << "You remove the light [fitting]" + to_chat(user, "You remove the light [get_fitting_name()]") else if(TK in user.mutations) - user << "You telekinetically remove the light [fitting]." + to_chat(user, "You telekinetically remove the light [get_fitting_name()].") else - user << "You try to remove the light [fitting], but it's too hot and you don't want to burn your hand." + to_chat(user, "You try to remove the light [get_fitting_name()], but it's too hot and you don't want to burn your hand.") return // if burned, don't remove the light else - user << "You remove the light [fitting]." + to_chat(user, "You remove the light [get_fitting_name()].") // create a light tube/bulb item and put it in the user's hand - var/obj/item/weapon/light/L = new light_type() - L.status = status - L.rigged = rigged - L.brightness_range = brightness_range - L.brightness_power = brightness_power - L.brightness_color = brightness_color - - // light item inherits the switchcount, then zero it - L.switchcount = switchcount - switchcount = 0 - - L.update() - L.add_fingerprint(user) - - user.put_in_active_hand(L) //puts it in our active hand - - status = LIGHT_EMPTY - update() + user.put_in_active_hand(remove_bulb()) //puts it in our active hand /obj/machinery/light/flamp/attack_hand(mob/user) if(lamp_shade) if(status == LIGHT_EMPTY) - user << "There is no [fitting] in this light." + to_chat(user, "There is no [get_fitting_name()] in this light.") return if(on) @@ -662,28 +648,11 @@ /obj/machinery/light/attack_tk(mob/user) if(status == LIGHT_EMPTY) - user << "There is no [fitting] in this light." + to_chat(user, "There is no [get_fitting_name()] in this light.") return - user << "You telekinetically remove the light [fitting]." - // create a light tube/bulb item and put it in the user's hand - var/obj/item/weapon/light/L = new light_type() - L.status = status - L.rigged = rigged - L.brightness_range = brightness_range - L.brightness_power = brightness_power - L.brightness_color = brightness_color - - // light item inherits the switchcount, then zero it - L.switchcount = switchcount - switchcount = 0 - - L.update() - L.add_fingerprint(user) - L.loc = loc - - status = LIGHT_EMPTY - update() + to_chat(user, "You telekinetically remove the light [get_fitting_name()].") + remove_bulb() // break the light and make sparks if was on @@ -730,13 +699,7 @@ // timed process // use power -#define LIGHTING_POWER_FACTOR 20 //20W per unit luminosity - - /obj/machinery/light/process() - if(on) - use_power(light_range * LIGHTING_POWER_FACTOR, LIGHT) - if(auto_flicker && !flickering) if(check_for_player_proximity(src, radius = 12, ignore_ghosts = FALSE, ignore_afk = TRUE)) seton(TRUE) // Lights must be on to flicker. @@ -744,7 +707,6 @@ else seton(FALSE) // Otherwise keep it dark and spooky for when someone shows up. - // called when area power state changes /obj/machinery/light/power_change() spawn(10) @@ -781,8 +743,10 @@ var/switchcount = 0 // number of times switched matter = list(DEFAULT_WALL_MATERIAL = 60) var/rigged = 0 // true if rigged to explode + var/broken_chance = 2 + var/brightness_range = 2 //how much light it gives off - var/brightness_power = 0.8 + var/brightness_power = 1 var/brightness_color = LIGHT_COLOR_INCANDESCENT_TUBE /obj/item/weapon/light/tube @@ -792,13 +756,14 @@ base_state = "ltube" item_state = "c_tube" matter = list("glass" = 100) - brightness_range = 8 + brightness_range = 10 // luminosity when on, also used in power calculation //VOREStation Edit + brightness_power = 6 /obj/item/weapon/light/tube/large w_class = ITEMSIZE_SMALL name = "large light tube" brightness_range = 15 - brightness_power = 0.9 + brightness_power = 9 /obj/item/weapon/light/bulb name = "light bulb" @@ -808,12 +773,18 @@ item_state = "contvapour" matter = list("glass" = 100) brightness_range = 5 + brightness_power = 4 brightness_color = LIGHT_COLOR_INCANDESCENT_BULB /obj/item/weapon/light/throw_impact(atom/hit_atom) ..() shatter() +/obj/item/weapon/light/bulb/red + brightness_range = 4 + color = "#da0205" + brightness_color = "#da0205" + /obj/item/weapon/light/bulb/fire name = "fire bulb" desc = "A replacement fire bulb." @@ -821,11 +792,9 @@ base_state = "fbulb" item_state = "egg4" matter = list("glass" = 100) - brightness_range = 5 // update the icon state and description of the light - -/obj/item/weapon/light/proc/update() +/obj/item/weapon/light/update_icon() switch(status) if(LIGHT_OK) icon_state = base_state @@ -838,14 +807,19 @@ desc = "A broken [name]." -/obj/item/weapon/light/New() +/obj/item/weapon/light/New(atom/newloc, obj/machinery/light/fixture = null) ..() - switch(name) - if("light tube") - brightness_range = rand(6,9) - if("light bulb") - brightness_range = rand(4,6) - update() + if(fixture) + status = fixture.status + rigged = fixture.rigged + switchcount = fixture.switchcount + fixture.transfer_fingerprints_to(src) + + //shouldn't be necessary to copy these unless someone varedits stuff, but just in case + brightness_range = fixture.brightness_range + brightness_power = fixture.brightness_power + brightness_color = fixture.brightness_color + update_icon() // attack bulb/tube with object @@ -855,7 +829,7 @@ if(istype(I, /obj/item/weapon/reagent_containers/syringe)) var/obj/item/weapon/reagent_containers/syringe/S = I - user << "You inject the solution into the [src]." + to_chat(user, "You inject the solution into the [src].") if(S.reagents.has_reagent("phoron", 5)) @@ -889,7 +863,7 @@ force = 5 sharp = 1 playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) - update() + update_icon() //Lamp Shade /obj/item/weapon/lampshade diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 5e4c2e6207..2443f3ea27 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -399,13 +399,13 @@ /obj/machinery/power/port_gen/pacman/super/UseFuel() //produces a tiny amount of radiation when in use if (prob(2*power_output)) - radiation_repository.radiate(src, 4) + SSradiation.radiate(src, 4) ..() /obj/machinery/power/port_gen/pacman/super/explode() //a nice burst of radiation var/rads = 50 + (sheets + sheet_left)*1.5 - radiation_repository.radiate(src, (max(20, rads))) + SSradiation.radiate(src, (max(20, rads))) explosion(src.loc, 3, 3, 5, 3) qdel(src) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 0bac07c2dc..1dc29c7fd7 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -32,7 +32,7 @@ var/global/list/rad_collectors = list() if(P && active) - var/rads = radiation_repository.get_rads_at_turf(get_turf(src)) + var/rads = SSradiation.get_rads_at_turf(get_turf(src)) if(rads) receive_pulse(rads * 5) //Maths is hard diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index a9fca3e160..921b000078 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -31,7 +31,7 @@ prepare_recipes() /obj/machinery/particle_smasher/Destroy() - for(var/datum/recipe/particle_smasher/D in recipes) + for(var/datum/particle_smasher_recipe/D in recipes) qdel(D) recipes.Cut() ..() @@ -142,13 +142,13 @@ /obj/machinery/particle_smasher/process() if(!src.anchored) // Rapidly loses focus. if(energy) - radiation_repository.radiate(src, round(((src.energy-150)/50)*5,1)) + SSradiation.radiate(src, round(((src.energy-150)/50)*5,1)) energy = max(0, energy - 30) update_icon() return if(energy) - radiation_repository.radiate(src, round(((src.energy-150)/50)*5,1)) + SSradiation.radiate(src, round(((src.energy-150)/50)*5,1)) energy = CLAMP(energy - 5, 0, max_energy) return @@ -156,19 +156,19 @@ /obj/machinery/particle_smasher/proc/prepare_recipes() if(!recipes) recipes = list() - for(var/D in subtypesof(/datum/recipe/particle_smasher)) + for(var/D in subtypesof(/datum/particle_smasher_recipe)) recipes += new D else - for(var/datum/recipe/particle_smasher/D in recipes) + for(var/datum/particle_smasher_recipe/D in recipes) qdel(D) recipes.Cut() - for(var/D in subtypesof(/datum/recipe/particle_smasher)) + for(var/D in subtypesof(/datum/particle_smasher_recipe)) recipes += new D /obj/machinery/particle_smasher/proc/TryCraft() if(!recipes || !recipes.len) - recipes = typesof(/datum/recipe/particle_smasher) + recipes = typesof(/datum/particle_smasher_recipe) if(!target) // You are just blasting an empty machine. visible_message("\The [src] shudders.") @@ -178,14 +178,14 @@ if(successful_craft) visible_message("\The [src] fizzles.") if(prob(33)) // Why are you blasting it after it's already done! - radiation_repository.radiate(src, 10 + round(src.energy / 60, 1)) + SSradiation.radiate(src, 10 + round(src.energy / 60, 1)) energy = max(0, energy - 30) update_icon() return var/list/possible_recipes = list() var/max_prob = 0 - for(var/datum/recipe/particle_smasher/R in recipes) // Only things for the smasher. Don't get things like the chef's cake recipes. + for(var/datum/particle_smasher_recipe/R in recipes) // Only things for the smasher. Don't get things like the chef's cake recipes. if(R.probability) // It's actually a recipe you're supposed to be able to make. if(istype(target, R.required_material)) if(energy >= R.required_energy_min && energy <= R.required_energy_max) // The machine has enough Vaguely Defined 'Energy'. @@ -204,7 +204,7 @@ if(possible_recipes.len) var/local_prob = rand(0, max_prob - 1)%max_prob var/cumulative = 0 - for(var/datum/recipe/particle_smasher/R in possible_recipes) + for(var/datum/particle_smasher_recipe/R in possible_recipes) cumulative += R.probability if(local_prob < cumulative) successful_craft = TRUE @@ -212,7 +212,7 @@ break update_icon() -/obj/machinery/particle_smasher/proc/DoCraft(var/datum/recipe/particle_smasher/recipe) +/obj/machinery/particle_smasher/proc/DoCraft(var/datum/particle_smasher_recipe/recipe) if(!successful_craft || !recipe) return @@ -260,11 +260,11 @@ * The special recipe datums used for the particle smasher. */ -/datum/recipe/particle_smasher - //reagents //Commented out due to inheritance. Still a list, used as ex: // example: = list("pacid" = 5) - //items //Commented out due to inheritance. Still a list, used as ex: // example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) Place /foo/bar before /foo. Do not include fruit. Maximum of 3 items. +/datum/particle_smasher_recipe + var/list/reagents // example: = list("pacid" = 5) + var/list/items // example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) Place /foo/bar before /foo. Do not include fruit. Maximum of 3 items. - result = /obj/item/stack/material/iron // The sheet this will produce. + var/result = /obj/item/stack/material/iron // The sheet this will produce. var/required_material = /obj/item/stack/material/iron // The required material sheet. var/required_energy_min = 0 // The minimum energy this recipe can process at. var/required_energy_max = 600 // The maximum energy this recipe can process at. @@ -272,7 +272,7 @@ var/required_atmos_temp_max = 600 // The maximum ambient atmospheric temperature required, in kelvin. var/probability = 0 // The probability for the recipe to be produced. 0 will make it impossible. -/datum/recipe/particle_smasher/check_items(var/obj/container as obj) +/datum/particle_smasher_recipe/proc/check_items(var/obj/container as obj) . = 1 if (items && items.len) var/list/checklist = list() @@ -295,7 +295,20 @@ . = -1 return . -/datum/recipe/particle_smasher/deuterium_tritium +/datum/particle_smasher_recipe/proc/check_reagents(var/datum/reagents/avail_reagents) + . = 1 + for (var/r_r in reagents) + var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) + if (!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals + if (aval_r_amnt>reagents[r_r]) + . = 0 + else + return -1 + if ((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + return 0 + return . + +/datum/particle_smasher_recipe/deuterium_tritium reagents = list("hydrogen" = 15) result = /obj/item/stack/material/tritium @@ -307,7 +320,7 @@ required_atmos_temp_max = 200 probability = 30 -/datum/recipe/particle_smasher/verdantium_morphium +/datum/particle_smasher_recipe/verdantium_morphium result = /obj/item/stack/material/morphium required_material = /obj/item/stack/material/verdantium @@ -315,7 +328,7 @@ required_energy_max = 500 probability = 20 -/datum/recipe/particle_smasher/plasteel_morphium +/datum/particle_smasher_recipe/plasteel_morphium items = list(/obj/item/prop/alien/junk) result = /obj/item/stack/material/morphium @@ -325,7 +338,7 @@ required_energy_max = 300 probability = 10 -/datum/recipe/particle_smasher/osmium_lead +/datum/particle_smasher_recipe/osmium_lead reagents = list("tungsten" = 10) result = /obj/item/stack/material/lead @@ -338,7 +351,7 @@ required_atmos_temp_max = 8000 probability = 50 -/datum/recipe/particle_smasher/phoron_valhollide +/datum/particle_smasher_recipe/phoron_valhollide reagents = list("phoron" = 10, "pacid" = 10) result = /obj/item/stack/material/valhollide @@ -351,7 +364,7 @@ required_atmos_temp_max = 100 probability = 10 -/datum/recipe/particle_smasher/valhollide_supermatter +/datum/particle_smasher_recipe/valhollide_supermatter reagents = list("phoron" = 300) result = /obj/item/stack/material/supermatter diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 3768ab9535..b75a81e1e4 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -408,7 +408,7 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) if (src.energy>200) toxdamage = round(((src.energy-150)/50)*4,1) radiation = round(((src.energy-150)/50)*5,1) - radiation_repository.radiate(src, radiation) //Always radiate at max, so a decent dose of radiation is applied + SSradiation.radiate(src, radiation) //Always radiate at max, so a decent dose of radiation is applied for(var/mob/living/M in view(toxrange, src.loc)) if(M.status_flags & GODMODE) continue @@ -451,7 +451,7 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) M << "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat." M << "You don't even have a moment to react as you are reduced to ashes by the intense radiation." M.dust() - radiation_repository.radiate(src, rand(energy)) + SSradiation.radiate(src, rand(energy)) return /obj/singularity/proc/pulse() diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index dae021e5cd..c4c7d2ff02 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -253,6 +253,7 @@ GLOBAL_LIST_EMPTY(solars_list) new /obj/machinery/power/tracker(get_turf(src), src) else new /obj/machinery/power/solar(get_turf(src), src) + qdel(src) else to_chat(user, "You need two sheets of glass to put them into a solar panel.") return diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index e436497fc5..9e1f3f06c2 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -90,14 +90,47 @@ var/datum/looping_sound/supermatter/soundloop +/obj/machinery/power/supermatter/New() + ..() + uid = gl_uid++ + /obj/machinery/power/supermatter/Initialize() soundloop = new(list(src), TRUE) return ..() /obj/machinery/power/supermatter/Destroy() + STOP_PROCESSING(SSobj, src) QDEL_NULL(soundloop) return ..() +/obj/machinery/power/supermatter/proc/get_status() + var/turf/T = get_turf(src) + if(!T) + return SUPERMATTER_ERROR + var/datum/gas_mixture/air = T.return_air() + if(!air) + return SUPERMATTER_ERROR + + if(grav_pulling || exploded) + return SUPERMATTER_DELAMINATING + + if(get_integrity() < 25) + return SUPERMATTER_EMERGENCY + + if(get_integrity() < 50) + return SUPERMATTER_DANGER + + if((get_integrity() < 100) || (air.temperature > CRITICAL_TEMPERATURE)) + return SUPERMATTER_WARNING + + if(air.temperature > (CRITICAL_TEMPERATURE * 0.8)) + return SUPERMATTER_NOTIFY + + if(power > 5) + return SUPERMATTER_NORMAL + return SUPERMATTER_INACTIVE + + /obj/machinery/power/supermatter/proc/explode() message_admins("Supermatter exploded at ([x],[y],[z] - JMP)",0,1) log_game("SUPERMATTER([x],[y],[z]) Exploded. Power:[power], Oxygen:[oxygen], Damage:[damage], Integrity:[get_integrity()]") @@ -108,7 +141,7 @@ if(!TS) return for(var/z in GetConnectedZlevels(TS.z)) - radiation_repository.z_radiate(locate(1, 1, z), DETONATION_RADS, 1) + SSradiation.z_radiate(locate(1, 1, z), DETONATION_RADS, 1) for(var/mob/living/mob in living_mob_list) var/turf/T = get_turf(mob) if(T && (loc.z == T.z)) @@ -118,6 +151,8 @@ H.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) ) spawn(pull_time) explosion(get_turf(src), explosion_power, explosion_power * 2, explosion_power * 3, explosion_power * 4, 1) + spawn(5) //to allow the explosion to finish + new /obj/item/broken_sm(TS) qdel(src) return @@ -136,6 +171,7 @@ /obj/machinery/power/supermatter/proc/announce_warning() var/integrity = get_integrity() var/alert_msg = " Integrity at [integrity]%" + var/message_sound = 'sound/ambience/matteralarm.ogg' // VOREStation Edit - Rykka adds SM Delam alarm if(damage > emergency_point) alert_msg = emergency_alert + alert_msg @@ -156,6 +192,9 @@ //Public alerts if((damage > emergency_point) && !public_alert) global_announcer.autosay("WARNING: SUPERMATTER CRYSTAL DELAMINATION IMMINENT!", "Supermatter Monitor") + for(var/mob/M in player_list) // VOREStation Edit - Rykka adds SM Delam alarm + if(!istype(M,/mob/new_player) && !isdeaf(M)) // VOREStation Edit - Rykka adds SM Delam alarm + M << message_sound // VOREStation Edit - Rykka adds SM Delam alarm admin_chat_message(message = "SUPERMATTER DELAMINATING!", color = "#FF2222") //VOREStation Add public_alert = 1 log_game("SUPERMATTER([x],[y],[z]) Emergency PUBLIC announcement. Power:[power], Oxygen:[oxygen], Damage:[damage], Integrity:[get_integrity()]") @@ -272,7 +311,7 @@ if(!istype(l.glasses, /obj/item/clothing/glasses/meson)) // VOREStation Edit - Only mesons can protect you! l.hallucination = max(0, min(200, l.hallucination + power * config_hallucination_power * sqrt( 1 / max(1,get_dist(l, src)) ) ) ) - radiation_repository.radiate(src, max(power * 1.5, 50) ) //Better close those shutters! + SSradiation.radiate(src, max(power * 1.5, 50) ) //Better close those shutters! power -= (power/DECAY_FACTOR)**3 //energy losses due to radiation @@ -385,7 +424,7 @@ else l.show_message("You hear an uneartly ringing and notice your skin is covered in fresh radiation burns.", 2) var/rads = 500 - radiation_repository.radiate(src, rads) + SSradiation.radiate(src, rads) /proc/supermatter_pull(var/atom/target, var/pull_range = 255, var/pull_power = STAGE_FIVE) for(var/atom/A in range(pull_range, target)) @@ -415,3 +454,21 @@ /obj/machinery/power/supermatter/shard/announce_warning() //Shards don't get announcements return + +/obj/item/broken_sm + name = "shattered supermatter plinth" + desc = "The shattered remains of a supermatter shard plinth. It doesn't look safe to be around." + icon = 'icons/obj/engine.dmi' + icon_state = "darkmatter_broken" + +/obj/item/broken_sm/New() + message_admins("Broken SM shard created at ([x],[y],[z] - JMP)",0,1) + START_PROCESSING(SSobj, src) + return ..() + +/obj/item/broken_sm/process() + SSradiation.radiate(src, 50) + +/obj/item/broken_sm/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 76029fd66c..ab54a82957 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -95,6 +95,7 @@ playsound(src.loc, 'sound/effects/lightningshock.ogg', 100, 1, extrarange = 5) tesla_zap(src, 10, power/(coeff/2)) +//TFF 3/6/19 - Port Cit RP fix for infinite frames /obj/machinery/power/grounding_rod name = "grounding rod" desc = "Keep an area from being fried from Edison's Bane." @@ -105,6 +106,7 @@ can_buckle = TRUE buckle_lying = FALSE + circuit = /obj/item/weapon/circuitboard/grounding_rod /obj/machinery/power/grounding_rod/pre_mapped anchored = TRUE diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index bbc13e381e..8f1c01be21 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -180,15 +180,15 @@ if(dna_lock && attached_lock.stored_dna) if(!authorized_user(user)) if(attached_lock.safety_level == 0) - to_chat(M, "\The [src] buzzes in dissapoint and displays an invalid DNA symbol.") + to_chat(M, "\The [src] buzzes in dissapointment and displays an invalid DNA symbol.") return 0 if(!attached_lock.exploding) if(attached_lock.safety_level == 1) to_chat(M, "\The [src] hisses in dissapointment.") visible_message("\The [src] announces, \"Self-destruct occurring in ten seconds.\"", "\The [src] announces, \"Self-destruct occurring in ten seconds.\"") + attached_lock.exploding = 1 spawn(100) explosion(src, 0, 0, 3, 4) - attached_lock.exploding = 1 sleep(1) qdel(src) return 0 @@ -692,18 +692,18 @@ var/obj/item/projectile/in_chamber = consume_next_projectile() if (istype(in_chamber)) user.visible_message("[user] pulls the trigger.") - play_fire_sound() - if(istype(in_chamber, /obj/item/projectile/beam/lastertag)) + play_fire_sound(M, in_chamber) + if(istype(in_chamber, /obj/item/projectile/beam/lasertag)) user.show_message("You feel rather silly, trying to commit suicide with a toy.") mouthshoot = 0 return in_chamber.on_hit(M) - if (in_chamber.damage_type != HALLOSS) + if(in_chamber.damage_type != HALLOSS && !in_chamber.nodamage) log_and_message_admins("[key_name(user)] commited suicide using \a [src]") user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, "head", used_weapon = "Point blank shot in the mouth with \a [in_chamber]", sharp=1) user.death() - else + else if(in_chamber.damage_type == HALLOSS) to_chat(user, "Ow...") user.apply_effect(110,AGONY,0) qdel(in_chamber) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index d69b7c7559..c28e2b0588 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -1,208 +1,230 @@ -/obj/item/weapon/gun/energy - name = "energy gun" - desc = "A basic energy-based gun." - icon_state = "energy" - fire_sound_text = "laser blast" - - var/obj/item/weapon/cell/power_supply //What type of power cell this uses - var/charge_cost = 240 //How much energy is needed to fire. - - var/accept_cell_type = /obj/item/weapon/cell/device - var/cell_type = /obj/item/weapon/cell/device/weapon - projectile_type = /obj/item/projectile/beam/practice - - var/modifystate - var/charge_meter = 1 //if set, the icon state will be chosen based on the current charge - - //self-recharging - var/self_recharge = 0 //if set, the weapon will recharge itself - var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically - var/recharge_time = 4 - var/charge_tick = 0 - var/charge_delay = 75 //delay between firing and charging - - var/battery_lock = 0 //If set, weapon cannot switch batteries - -/obj/item/weapon/gun/energy/New() - ..() - if(self_recharge) - power_supply = new /obj/item/weapon/cell/device/weapon(src) - START_PROCESSING(SSobj, src) - else - if(cell_type) - power_supply = new cell_type(src) - else - power_supply = null - - update_icon() - -/obj/item/weapon/gun/energy/Destroy() - if(self_recharge) - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/weapon/gun/energy/get_cell() - return power_supply - -/obj/item/weapon/gun/energy/process() - if(self_recharge) //Every [recharge_time] ticks, recharge a shot for the battery - if(world.time > last_shot + charge_delay) //Doesn't work if you've fired recently - if(!power_supply || power_supply.charge >= power_supply.maxcharge) - return 0 // check if we actually need to recharge - - charge_tick++ - if(charge_tick < recharge_time) return 0 - charge_tick = 0 - - var/rechargeamt = power_supply.maxcharge*0.2 - - if(use_external_power) - var/obj/item/weapon/cell/external = get_external_power_supply() - if(!external || !external.use(rechargeamt)) //Take power from the borg... - return 0 - - power_supply.give(rechargeamt) //... to recharge 1/5th the battery - update_icon() - else - charge_tick = 0 - return 1 - -/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob) - ..() - -/obj/item/weapon/gun/energy/switch_firemodes(mob/user) - if(..()) - update_icon() - -/obj/item/weapon/gun/energy/emp_act(severity) - ..() - update_icon() - -/obj/item/weapon/gun/energy/consume_next_projectile() - if(!power_supply) return null - if(!ispath(projectile_type)) return null - if(!power_supply.checked_use(charge_cost)) return null - return new projectile_type(src) - -/obj/item/weapon/gun/energy/proc/load_ammo(var/obj/item/C, mob/user) - if(istype(C, /obj/item/weapon/cell)) - if(self_recharge || battery_lock) - user << "[src] does not have a battery port." - return - if(istype(C, accept_cell_type)) - var/obj/item/weapon/cell/P = C - if(power_supply) - user << "[src] already has a power cell." - else - user.visible_message("[user] is reloading [src].", "You start to insert [P] into [src].") - if(do_after(user, 5 * P.w_class)) - user.remove_from_mob(P) - power_supply = P - P.loc = src - user.visible_message("[user] inserts [P] into [src].", "You insert [P] into [src].") - playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) - update_icon() - update_held_icon() - else - user << "This cell is not fitted for [src]." - return - -/obj/item/weapon/gun/energy/proc/unload_ammo(mob/user) - if(self_recharge || battery_lock) - user << "[src] does not have a battery port." - return - if(power_supply) - user.put_in_hands(power_supply) - power_supply.update_icon() - user.visible_message("[user] removes [power_supply] from [src].", "You remove [power_supply] from [src].") - power_supply = null - playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) - update_icon() - update_held_icon() - else - user << "[src] does not have a power cell." - -/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob) - ..() - load_ammo(A, user) - -/obj/item/weapon/gun/energy/attack_hand(mob/user as mob) - if(user.get_inactive_hand() == src) - unload_ammo(user) - else - return ..() - -/obj/item/weapon/gun/energy/proc/get_external_power_supply() - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - return R.cell - if(istype(src.loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = src.loc - if(module.holder && module.holder.wearer) - var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/weapon/rig/suit = H.back - if(istype(suit)) - return suit.cell - return null - -/obj/item/weapon/gun/energy/examine(mob/user) - . = ..() - if(power_supply) - if(charge_cost) - var/shots_remaining = round(power_supply.charge / max(1, charge_cost)) // Paranoia - to_chat(user, "Has [shots_remaining] shot\s remaining.") - else - to_chat(user, "Has infinite shots remaining.") - else - to_chat(user, "Does not have a power cell.") - return - -/obj/item/weapon/gun/energy/update_icon(var/ignore_inhands) - if(power_supply == null) - if(modifystate) - icon_state = "[modifystate]_open" - else - icon_state = "[initial(icon_state)]_open" - return - else if(charge_meter) - var/ratio = power_supply.charge / power_supply.maxcharge - - //make sure that rounding down will not give us the empty state even if we have charge for a shot left. - if(power_supply.charge < charge_cost) - ratio = 0 - else - ratio = max(round(ratio, 0.25) * 100, 25) - - if(modifystate) - icon_state = "[modifystate][ratio]" - else - icon_state = "[initial(icon_state)][ratio]" - - else if(power_supply) - if(modifystate) - icon_state = "[modifystate]" - else - icon_state = "[initial(icon_state)]" - - if(!ignore_inhands) update_held_icon() - -/obj/item/weapon/gun/energy/proc/start_recharge() - if(power_supply == null) - power_supply = new /obj/item/weapon/cell/device/weapon(src) - self_recharge = 1 - START_PROCESSING(SSobj, src) - update_icon() - -/obj/item/weapon/gun/energy/get_description_interaction() - var/list/results = list() - - if(!battery_lock && !self_recharge) - if(power_supply) - results += "[desc_panel_image("offhand")]to remove the weapon cell." - else - results += "[desc_panel_image("weapon cell")]to add a new weapon cell." - - results += ..() - - return results \ No newline at end of file +/obj/item/weapon/gun/energy + name = "energy gun" + desc = "A basic energy-based gun." + icon_state = "energy" + fire_sound_text = "laser blast" + + var/obj/item/weapon/cell/power_supply //What type of power cell this uses + var/charge_cost = 240 //How much energy is needed to fire. + + var/accept_cell_type = /obj/item/weapon/cell/device + var/cell_type = /obj/item/weapon/cell/device/weapon + projectile_type = /obj/item/projectile/beam/practice + + var/modifystate + var/charge_meter = 1 //if set, the icon state will be chosen based on the current charge + + //self-recharging + var/self_recharge = 0 //if set, the weapon will recharge itself + var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically + var/use_organic_power = 0 // If set, the weapon will draw from nutrition or blood. + var/recharge_time = 4 + var/charge_tick = 0 + var/charge_delay = 75 //delay between firing and charging + + var/battery_lock = 0 //If set, weapon cannot switch batteries + +/obj/item/weapon/gun/energy/New() + ..() + if(self_recharge) + power_supply = new /obj/item/weapon/cell/device/weapon(src) + START_PROCESSING(SSobj, src) + else + if(cell_type) + power_supply = new cell_type(src) + else + power_supply = null + + update_icon() + +/obj/item/weapon/gun/energy/Destroy() + if(self_recharge) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/gun/energy/get_cell() + return power_supply + +/obj/item/weapon/gun/energy/process() + if(self_recharge) //Every [recharge_time] ticks, recharge a shot for the battery + if(world.time > last_shot + charge_delay) //Doesn't work if you've fired recently + if(!power_supply || power_supply.charge >= power_supply.maxcharge) + return 0 // check if we actually need to recharge + + charge_tick++ + if(charge_tick < recharge_time) return 0 + charge_tick = 0 + + var/rechargeamt = power_supply.maxcharge*0.2 + + if(use_external_power) + var/obj/item/weapon/cell/external = get_external_power_supply() + if(!external || !external.use(rechargeamt)) //Take power from the borg... + return 0 + + if(use_organic_power) + var/mob/living/carbon/human/H + if(ishuman(loc)) + H = loc + + if(istype(loc, /obj/item/organ)) + var/obj/item/organ/O = loc + if(O.owner) + H = O.owner + + if(istype(H)) + var/start_nutrition = H.nutrition + var/end_nutrition = 0 + + H.nutrition -= rechargeamt / 10 + + end_nutrition = H.nutrition + + if(start_nutrition - max(0, end_nutrition) < rechargeamt / 10) + H.remove_blood((rechargeamt / 10) - (start_nutrition - max(0, end_nutrition))) + + power_supply.give(rechargeamt) //... to recharge 1/5th the battery + update_icon() + else + charge_tick = 0 + return 1 + +/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob) + ..() + +/obj/item/weapon/gun/energy/switch_firemodes(mob/user) + if(..()) + update_icon() + +/obj/item/weapon/gun/energy/emp_act(severity) + ..() + update_icon() + +/obj/item/weapon/gun/energy/consume_next_projectile() + if(!power_supply) return null + if(!ispath(projectile_type)) return null + if(!power_supply.checked_use(charge_cost)) return null + return new projectile_type(src) + +/obj/item/weapon/gun/energy/proc/load_ammo(var/obj/item/C, mob/user) + if(istype(C, /obj/item/weapon/cell)) + if(self_recharge || battery_lock) + user << "[src] does not have a battery port." + return + if(istype(C, accept_cell_type)) + var/obj/item/weapon/cell/P = C + if(power_supply) + user << "[src] already has a power cell." + else + user.visible_message("[user] is reloading [src].", "You start to insert [P] into [src].") + if(do_after(user, 5 * P.w_class)) + user.remove_from_mob(P) + power_supply = P + P.loc = src + user.visible_message("[user] inserts [P] into [src].", "You insert [P] into [src].") + playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) + update_icon() + update_held_icon() + else + user << "This cell is not fitted for [src]." + return + +/obj/item/weapon/gun/energy/proc/unload_ammo(mob/user) + if(self_recharge || battery_lock) + user << "[src] does not have a battery port." + return + if(power_supply) + user.put_in_hands(power_supply) + power_supply.update_icon() + user.visible_message("[user] removes [power_supply] from [src].", "You remove [power_supply] from [src].") + power_supply = null + playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) + update_icon() + update_held_icon() + else + user << "[src] does not have a power cell." + +/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob) + ..() + load_ammo(A, user) + +/obj/item/weapon/gun/energy/attack_hand(mob/user as mob) + if(user.get_inactive_hand() == src) + unload_ammo(user) + else + return ..() + +/obj/item/weapon/gun/energy/proc/get_external_power_supply() + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return R.cell + if(istype(src.loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = src.loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.back) + var/obj/item/weapon/rig/suit = H.back + if(istype(suit)) + return suit.cell + return null + +/obj/item/weapon/gun/energy/examine(mob/user) + . = ..() + if(power_supply) + if(charge_cost) + var/shots_remaining = round(power_supply.charge / max(1, charge_cost)) // Paranoia + to_chat(user, "Has [shots_remaining] shot\s remaining.") + else + to_chat(user, "Has infinite shots remaining.") + else + to_chat(user, "Does not have a power cell.") + return + +/obj/item/weapon/gun/energy/update_icon(var/ignore_inhands) + if(power_supply == null) + if(modifystate) + icon_state = "[modifystate]_open" + else + icon_state = "[initial(icon_state)]_open" + return + else if(charge_meter) + var/ratio = power_supply.charge / power_supply.maxcharge + + //make sure that rounding down will not give us the empty state even if we have charge for a shot left. + if(power_supply.charge < charge_cost) + ratio = 0 + else + ratio = max(round(ratio, 0.25) * 100, 25) + + if(modifystate) + icon_state = "[modifystate][ratio]" + else + icon_state = "[initial(icon_state)][ratio]" + + else if(power_supply) + if(modifystate) + icon_state = "[modifystate]" + else + icon_state = "[initial(icon_state)]" + + if(!ignore_inhands) update_held_icon() + +/obj/item/weapon/gun/energy/proc/start_recharge() + if(power_supply == null) + power_supply = new /obj/item/weapon/cell/device/weapon(src) + self_recharge = 1 + START_PROCESSING(SSobj, src) + update_icon() + +/obj/item/weapon/gun/energy/get_description_interaction() + var/list/results = list() + + if(!battery_lock && !self_recharge) + if(power_supply) + results += "[desc_panel_image("offhand")]to remove the weapon cell." + else + results += "[desc_panel_image("weapon cell")]to add a new weapon cell." + + results += ..() + + return results diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 6d7bdfef7a..f6321947b2 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -1,247 +1,266 @@ -/obj/item/weapon/gun/energy/laser - name = "laser rifle" - desc = "A Hephaestus Industries G40E rifle, designed to kill with concentrated energy blasts. This variant has the ability to \ - switch between standard fire and a more efficent but weaker 'suppressive' fire." - icon_state = "laser" - item_state = "laser" - wielded_item_state = "laser-wielded" - fire_delay = 8 - slot_flags = SLOT_BELT|SLOT_BACK - w_class = ITEMSIZE_LARGE - force = 10 - origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2) - matter = list(DEFAULT_WALL_MATERIAL = 2000) - projectile_type = /obj/item/projectile/beam/midlaser -// one_handed_penalty = 30 - - firemodes = list( - list(mode_name="normal", fire_delay=8, projectile_type=/obj/item/projectile/beam/midlaser, charge_cost = 240), - list(mode_name="suppressive", fire_delay=5, projectile_type=/obj/item/projectile/beam/weaklaser, charge_cost = 60), - ) - -/obj/item/weapon/gun/energy/laser/mounted - self_recharge = 1 - use_external_power = 1 - one_handed_penalty = 0 // Not sure if two-handing gets checked for mounted weapons, but better safe than sorry. - -/obj/item/weapon/gun/energy/laser/practice - name = "practice laser carbine" - desc = "A modified version of the HI G40E, this one fires less concentrated energy bolts designed for target practice." - projectile_type = /obj/item/projectile/beam/practice - charge_cost = 48 - - cell_type = /obj/item/weapon/cell/device - - firemodes = list( - list(mode_name="normal", projectile_type=/obj/item/projectile/beam/practice, charge_cost = 48), - list(mode_name="suppressive", projectile_type=/obj/item/projectile/beam/practice, charge_cost = 12), - ) - -/obj/item/weapon/gun/energy/retro - name = "retro laser" - icon_state = "retro" - item_state = "retro" - desc = "An older model of the basic lasergun. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." - slot_flags = SLOT_BELT - w_class = ITEMSIZE_NORMAL - projectile_type = /obj/item/projectile/beam - fire_delay = 10 //old technology - -/obj/item/weapon/gun/energy/retro/mounted - self_recharge = 1 - use_external_power = 1 - -/obj/item/weapon/gun/energy/retro/empty - icon_state = "retro" - cell_type = null - - -/datum/category_item/catalogue/anomalous/precursor_a/alien_pistol - name = "Precursor Alpha Weapon - Appendageheld Laser" - desc = "This object strongly resembles a weapon, and if one were to pull the \ - trigger located on the handle of the object, it would fire a deadly \ - laser at whatever it was pointed at. The beam fired appears to cause too \ - much damage to whatever it would hit to have served as a long ranged repair tool, \ - therefore this object was most likely designed to be a deadly weapon. If so, this \ - has several implications towards its creators;\ -

      \ - Firstly, it implies that these precursors, at some point during their development, \ - had needed to defend themselves, or otherwise had a need to utilize violence, and \ - as such created better tools to do so. It is unclear if violence was employed against \ - themselves as a form of in-fighting, or if violence was exclusive to outside species.\ -

      \ - Secondly, the shape and design of the weapon implies that the creators of this \ - weapon were able to grasp objects, and be able to manipulate the trigger independently \ - from merely holding onto the weapon, making certain types of appendages like tentacles be \ - unlikely.\ -

      \ - An interesting note about this weapon, when compared to contemporary energy weapons, is \ - that this gun appears to be inferior to modern laser weapons. The beam fired has less \ - of an ability to harm, and the power consumption appears to be higher than average for \ - a human-made energy side-arm. One possible explaination is that the creators of this \ - weapon, in their later years, had less of a need to optimize their capability for war, \ - and instead focused on other endeavors. Another explaination is that vast age of the weapon \ - may have caused it to degrade, yet still remain functional at a reduced capability." - value = CATALOGUER_REWARD_MEDIUM - -/obj/item/weapon/gun/energy/alien - name = "alien pistol" - desc = "A weapon that works very similarly to a traditional energy weapon. How this came to be will likely be a mystery for the ages." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_pistol) - icon_state = "alienpistol" - item_state = "alienpistol" - fire_sound = 'sound/weapons/eLuger.ogg' - fire_delay = 10 // Handguns should be inferior to two-handed weapons. Even alien ones I suppose. - charge_cost = 480 // Five shots. - - projectile_type = /obj/item/projectile/beam/cyan - cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien // Self charges. - origin_tech = list(TECH_COMBAT = 8, TECH_MAGNET = 7) - modifystate = "alienpistol" - - -/obj/item/weapon/gun/energy/captain - name = "antique laser gun" - icon_state = "caplaser" - item_state = "caplaser" - desc = "A rare weapon, handcrafted by a now defunct specialty manufacturer on Luna for a small fortune. It's certainly aged well." - force = 5 - slot_flags = SLOT_BELT - w_class = ITEMSIZE_NORMAL - projectile_type = /obj/item/projectile/beam - origin_tech = null - fire_delay = 10 //Old pistol - charge_cost = 480 //to compensate a bit for self-recharging - cell_type = /obj/item/weapon/cell/device/weapon/recharge/captain - battery_lock = 1 - -/obj/item/weapon/gun/energy/lasercannon - name = "laser cannon" - desc = "With the laser cannon, the lasing medium is enclosed in a tube lined with uranium-235 and subjected to high neutron \ - flux in a nuclear reactor core. This incredible technology may help YOU achieve high excitation rates with small laser volumes!" - icon_state = "lasercannon" - item_state = null - origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) - slot_flags = SLOT_BELT|SLOT_BACK - projectile_type = /obj/item/projectile/beam/heavylaser/cannon - battery_lock = 1 - fire_delay = 20 - w_class = ITEMSIZE_LARGE -// one_handed_penalty = 90 // The thing's heavy and huge. - accuracy = 45 - charge_cost = 600 - -/obj/item/weapon/gun/energy/lasercannon/mounted - name = "mounted laser cannon" - self_recharge = 1 - use_external_power = 1 - recharge_time = 10 - accuracy = 0 // Mounted cannons are just fine the way they are. - one_handed_penalty = 0 // Not sure if two-handing gets checked for mounted weapons, but better safe than sorry. - projectile_type = /obj/item/projectile/beam/heavylaser - charge_cost = 400 - fire_delay = 20 - -/obj/item/weapon/gun/energy/xray - name = "xray laser gun" - desc = "A high-power laser gun capable of expelling concentrated xray blasts, which are able to penetrate matter easier than \ - standard photonic beams, resulting in an effective 'anti-armor' energy weapon." - icon_state = "xray" - item_state = "xray" - origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) - projectile_type = /obj/item/projectile/beam/xray - charge_cost = 200 - -/obj/item/weapon/gun/energy/sniperrifle - name = "marksman energy rifle" - desc = "The HI DMR 9E is an older design of Hephaestus Industries. A designated marksman rifle capable of shooting powerful \ - ionized beams, this is a weapon to kill from a distance." - icon_state = "sniper" - item_state = "sniper" - item_state_slots = list(slot_r_hand_str = "z8carbine", slot_l_hand_str = "z8carbine") //placeholder - origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 5, TECH_POWER = 4) - projectile_type = /obj/item/projectile/beam/sniper - slot_flags = SLOT_BACK - battery_lock = 1 - charge_cost = 600 - fire_delay = 35 - force = 10 - w_class = ITEMSIZE_HUGE // So it can't fit in a backpack. - accuracy = -45 //shooting at the hip - scoped_accuracy = 0 -// requires_two_hands = 1 -// one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. - -/obj/item/weapon/gun/energy/sniperrifle/verb/scope() - set category = "Object" - set name = "Use Scope" - set popup_menu = 1 - - toggle_scope(2.0) - -/obj/item/weapon/gun/energy/monorifle - name = "antique mono-rifle" - desc = "An old laser rifle. This one can only fire once before requiring recharging." - description_fluff = "Modeled after ancient hunting rifles, this rifle was dubbed the 'Rainy Day Special' by some, due to its use as some barmens' fight-stopper of choice. One shot is all it takes, or so they say." - icon_state = "eshotgun" - item_state = "shotgun" - origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_POWER = 3) - projectile_type = /obj/item/projectile/beam/sniper - slot_flags = SLOT_BACK - charge_cost = 1300 - fire_delay = 20 - force = 8 - w_class = ITEMSIZE_LARGE - accuracy = 10 - scoped_accuracy = 15 - var/scope_multiplier = 1.5 - -/obj/item/weapon/gun/energy/monorifle/verb/sights() - set category = "Object" - set name = "Aim Down Sights" - set popup_menu = 1 - - toggle_scope(scope_multiplier) - -/obj/item/weapon/gun/energy/monorifle/combat - name = "combat mono-rifle" - desc = "A modernized version of the mono-rifle. This one can fire twice before requiring recharging." - description_fluff = "A modern design produced by a company once working from Saint Columbia, based on the antique mono-rifle 'Rainy Day Special' design." - icon_state = "ecshotgun" - item_state = "cshotgun" - charge_cost = 1000 - force = 12 - accuracy = 0 - scoped_accuracy = 20 - -////////Laser Tag//////////////////// - -/obj/item/weapon/gun/energy/lasertag - name = "laser tag gun" - item_state = "laser" - desc = "Standard issue weapon of the Imperial Guard" - origin_tech = list(TECH_COMBAT = 1, TECH_MAGNET = 2) - matter = list(DEFAULT_WALL_MATERIAL = 2000) - projectile_type = /obj/item/projectile/beam/lastertag/blue - cell_type = /obj/item/weapon/cell/device/weapon/recharge - battery_lock = 1 - var/required_vest - -/obj/item/weapon/gun/energy/lasertag/special_check(var/mob/living/carbon/human/M) - if(ishuman(M)) - if(!istype(M.wear_suit, required_vest)) - M << "You need to be wearing your laser tag vest!" - return 0 - return ..() - -/obj/item/weapon/gun/energy/lasertag/blue - icon_state = "bluetag" - item_state = "bluetag" - projectile_type = /obj/item/projectile/beam/lastertag/blue - required_vest = /obj/item/clothing/suit/bluetag - -/obj/item/weapon/gun/energy/lasertag/red - icon_state = "redtag" - item_state = "redtag" - projectile_type = /obj/item/projectile/beam/lastertag/red - required_vest = /obj/item/clothing/suit/redtag +/obj/item/weapon/gun/energy/laser + name = "laser rifle" + desc = "A Hephaestus Industries G40E rifle, designed to kill with concentrated energy blasts. This variant has the ability to \ + switch between standard fire and a more efficent but weaker 'suppressive' fire." + icon_state = "laser" + item_state = "laser" + wielded_item_state = "laser-wielded" + fire_delay = 8 + slot_flags = SLOT_BELT|SLOT_BACK + w_class = ITEMSIZE_LARGE + force = 10 + origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2) + matter = list(DEFAULT_WALL_MATERIAL = 2000) + projectile_type = /obj/item/projectile/beam/midlaser +// one_handed_penalty = 30 + + firemodes = list( + list(mode_name="normal", fire_delay=8, projectile_type=/obj/item/projectile/beam/midlaser, charge_cost = 240), + list(mode_name="suppressive", fire_delay=5, projectile_type=/obj/item/projectile/beam/weaklaser, charge_cost = 60), + ) + +/obj/item/weapon/gun/energy/laser/mounted + self_recharge = 1 + use_external_power = 1 + one_handed_penalty = 0 // Not sure if two-handing gets checked for mounted weapons, but better safe than sorry. + +/obj/item/weapon/gun/energy/laser/mounted/augment + use_external_power = FALSE + use_organic_power = TRUE + canremove = FALSE + +/obj/item/weapon/gun/energy/laser/practice + name = "practice laser carbine" + desc = "A modified version of the HI G40E, this one fires less concentrated energy bolts designed for target practice." + projectile_type = /obj/item/projectile/beam/practice + charge_cost = 48 + + cell_type = /obj/item/weapon/cell/device + + firemodes = list( + list(mode_name="normal", projectile_type=/obj/item/projectile/beam/practice, charge_cost = 48), + list(mode_name="suppressive", projectile_type=/obj/item/projectile/beam/practice, charge_cost = 12), + ) + +/obj/item/weapon/gun/energy/retro + name = "retro laser" + icon_state = "retro" + item_state = "retro" + desc = "An older model of the basic lasergun. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." + slot_flags = SLOT_BELT + w_class = ITEMSIZE_NORMAL + projectile_type = /obj/item/projectile/beam + fire_delay = 10 //old technology + +/obj/item/weapon/gun/energy/retro/mounted + self_recharge = 1 + use_external_power = 1 + +/obj/item/weapon/gun/energy/retro/empty + icon_state = "retro" + cell_type = null + + +/datum/category_item/catalogue/anomalous/precursor_a/alien_pistol + name = "Precursor Alpha Weapon - Appendageheld Laser" + desc = "This object strongly resembles a weapon, and if one were to pull the \ + trigger located on the handle of the object, it would fire a deadly \ + laser at whatever it was pointed at. The beam fired appears to cause too \ + much damage to whatever it would hit to have served as a long ranged repair tool, \ + therefore this object was most likely designed to be a deadly weapon. If so, this \ + has several implications towards its creators;\ +

      \ + Firstly, it implies that these precursors, at some point during their development, \ + had needed to defend themselves, or otherwise had a need to utilize violence, and \ + as such created better tools to do so. It is unclear if violence was employed against \ + themselves as a form of in-fighting, or if violence was exclusive to outside species.\ +

      \ + Secondly, the shape and design of the weapon implies that the creators of this \ + weapon were able to grasp objects, and be able to manipulate the trigger independently \ + from merely holding onto the weapon, making certain types of appendages like tentacles be \ + unlikely.\ +

      \ + An interesting note about this weapon, when compared to contemporary energy weapons, is \ + that this gun appears to be inferior to modern laser weapons. The beam fired has less \ + of an ability to harm, and the power consumption appears to be higher than average for \ + a human-made energy side-arm. One possible explaination is that the creators of this \ + weapon, in their later years, had less of a need to optimize their capability for war, \ + and instead focused on other endeavors. Another explaination is that vast age of the weapon \ + may have caused it to degrade, yet still remain functional at a reduced capability." + value = CATALOGUER_REWARD_MEDIUM + +/obj/item/weapon/gun/energy/alien + name = "alien pistol" + desc = "A weapon that works very similarly to a traditional energy weapon. How this came to be will likely be a mystery for the ages." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_pistol) + icon_state = "alienpistol" + item_state = "alienpistol" + fire_delay = 10 // Handguns should be inferior to two-handed weapons. Even alien ones I suppose. + charge_cost = 480 // Five shots. + + projectile_type = /obj/item/projectile/beam/cyan + cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien // Self charges. + origin_tech = list(TECH_COMBAT = 8, TECH_MAGNET = 7) + modifystate = "alienpistol" + + +/obj/item/weapon/gun/energy/captain + name = "antique laser gun" + icon_state = "caplaser" + item_state = "caplaser" + desc = "A rare weapon, handcrafted by a now defunct specialty manufacturer on Luna for a small fortune. It's certainly aged well." + force = 5 + slot_flags = SLOT_BELT + w_class = ITEMSIZE_NORMAL + projectile_type = /obj/item/projectile/beam + origin_tech = null + fire_delay = 10 //Old pistol + charge_cost = 480 //to compensate a bit for self-recharging + cell_type = /obj/item/weapon/cell/device/weapon/recharge/captain + battery_lock = 1 + +/obj/item/weapon/gun/energy/lasercannon + name = "laser cannon" + desc = "With the laser cannon, the lasing medium is enclosed in a tube lined with uranium-235 and subjected to high neutron \ + flux in a nuclear reactor core. This incredible technology may help YOU achieve high excitation rates with small laser volumes!" + icon_state = "lasercannon" + item_state = null + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) + slot_flags = SLOT_BELT|SLOT_BACK + projectile_type = /obj/item/projectile/beam/heavylaser/cannon + battery_lock = 1 + fire_delay = 20 + w_class = ITEMSIZE_LARGE +// one_handed_penalty = 90 // The thing's heavy and huge. + accuracy = 45 + charge_cost = 600 + +/obj/item/weapon/gun/energy/lasercannon/mounted + name = "mounted laser cannon" + self_recharge = 1 + use_external_power = 1 + recharge_time = 10 + accuracy = 0 // Mounted cannons are just fine the way they are. + one_handed_penalty = 0 // Not sure if two-handing gets checked for mounted weapons, but better safe than sorry. + projectile_type = /obj/item/projectile/beam/heavylaser + charge_cost = 400 + fire_delay = 20 + +/obj/item/weapon/gun/energy/xray + name = "xray laser gun" + desc = "A high-power laser gun capable of expelling concentrated xray blasts, which are able to penetrate matter easier than \ + standard photonic beams, resulting in an effective 'anti-armor' energy weapon." + icon_state = "xray" + item_state = "xray" + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) + projectile_type = /obj/item/projectile/beam/xray + charge_cost = 200 + +/obj/item/weapon/gun/energy/sniperrifle + name = "marksman energy rifle" + desc = "The HI DMR 9E is an older design of Hephaestus Industries. A designated marksman rifle capable of shooting powerful \ + ionized beams, this is a weapon to kill from a distance." + icon_state = "sniper" + item_state = "sniper" + item_state_slots = list(slot_r_hand_str = "z8carbine", slot_l_hand_str = "z8carbine") //placeholder + origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 5, TECH_POWER = 4) + projectile_type = /obj/item/projectile/beam/sniper + slot_flags = SLOT_BACK + battery_lock = 1 + charge_cost = 600 + fire_delay = 35 + force = 10 + w_class = ITEMSIZE_HUGE // So it can't fit in a backpack. + accuracy = -45 //shooting at the hip + scoped_accuracy = 0 +// requires_two_hands = 1 +// one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. + +/obj/item/weapon/gun/energy/sniperrifle/verb/scope() + set category = "Object" + set name = "Use Scope" + set popup_menu = 1 + + toggle_scope(2.0) + +/obj/item/weapon/gun/energy/monorifle + name = "antique mono-rifle" + desc = "An old laser rifle. This one can only fire once before requiring recharging." + description_fluff = "Modeled after ancient hunting rifles, this rifle was dubbed the 'Rainy Day Special' by some, due to its use as some barmens' fight-stopper of choice. One shot is all it takes, or so they say." + icon_state = "eshotgun" + item_state = "shotgun" + origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_POWER = 3) + projectile_type = /obj/item/projectile/beam/sniper + slot_flags = SLOT_BACK + charge_cost = 1300 + fire_delay = 20 + force = 8 + w_class = ITEMSIZE_LARGE + accuracy = 10 + scoped_accuracy = 15 + var/scope_multiplier = 1.5 + +/obj/item/weapon/gun/energy/monorifle/verb/sights() + set category = "Object" + set name = "Aim Down Sights" + set popup_menu = 1 + + toggle_scope(scope_multiplier) + +/obj/item/weapon/gun/energy/monorifle/combat + name = "combat mono-rifle" + desc = "A modernized version of the mono-rifle. This one can fire twice before requiring recharging." + description_fluff = "A modern design produced by a company once working from Saint Columbia, based on the antique mono-rifle 'Rainy Day Special' design." + icon_state = "ecshotgun" + item_state = "cshotgun" + charge_cost = 1000 + force = 12 + accuracy = 0 + scoped_accuracy = 20 + +////////Laser Tag//////////////////// + +/obj/item/weapon/gun/energy/lasertag + name = "laser tag gun" + item_state = "laser" + desc = "Standard issue weapon of the Imperial Guard" + origin_tech = list(TECH_COMBAT = 1, TECH_MAGNET = 2) + matter = list(DEFAULT_WALL_MATERIAL = 2000) + projectile_type = /obj/item/projectile/beam/lasertag/blue + cell_type = /obj/item/weapon/cell/device/weapon/recharge + battery_lock = 1 + var/required_vest + +/obj/item/weapon/gun/energy/lasertag/special_check(var/mob/living/carbon/human/M) + if(ishuman(M)) + if(!istype(M.wear_suit, required_vest)) + M << "You need to be wearing your laser tag vest!" + return 0 + return ..() + +/obj/item/weapon/gun/energy/lasertag/blue + icon_state = "bluetag" + item_state = "bluetag" + projectile_type = /obj/item/projectile/beam/lasertag/blue + required_vest = /obj/item/clothing/suit/bluetag + +/obj/item/weapon/gun/energy/lasertag/red + icon_state = "redtag" + item_state = "redtag" + projectile_type = /obj/item/projectile/beam/lasertag/red + required_vest = /obj/item/clothing/suit/redtag + +/obj/item/weapon/gun/energy/lasertag/omni + projectile_type = /obj/item/projectile/beam/lasertag/omni + +// Laser scattergun, proof of concept. + +/obj/item/weapon/gun/energy/lasershotgun + name = "laser scattergun" + icon = 'icons/obj/energygun.dmi' + item_state = "laser" + icon_state = "scatter" + desc = "A strange Almachi weapon, utilizing a refracting prism to turn a single laser blast into a diverging cluster." + origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 1, TECH_MATERIAL = 4) + + projectile_type = /obj/item/projectile/scatter/laser \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/special_vr.dm b/code/modules/projectiles/guns/energy/special_vr.dm index e1cfcaf5c9..73e878eed6 100644 --- a/code/modules/projectiles/guns/energy/special_vr.dm +++ b/code/modules/projectiles/guns/energy/special_vr.dm @@ -2,4 +2,26 @@ projectile_type = /obj/item/projectile/ion/pistol // still packs a punch but no AoE /obj/item/weapon/gun/energy/ionrifle/weak - projectile_type = /obj/item/projectile/ion/small \ No newline at end of file + projectile_type = /obj/item/projectile/ion/small + +/obj/item/weapon/gun/energy/medigun //Adminspawn/ERT etc + name = "directed restoration system" + desc = "The BL-3 'Phoenix' is an adaptation on the ML-3 'Medbeam' design that channels the power of the beam into a single healing laser. It is highly energy-inefficient, but its medical power cannot be denied." + force = 5 + icon_state = "medbeam" + item_state = "medbeam" + icon = 'icons/obj/gun_vr.dmi' + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_guns_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_guns_vr.dmi', + ) + slot_flags = SLOT_BELT + accuracy = 100 + fire_delay = 12 + fire_sound = 'sound/weapons/eluger.ogg' + + projectile_type = /obj/item/projectile/beam/medigun + + accept_cell_type = /obj/item/weapon/cell + cell_type = /obj/item/weapon/cell/high + charge_cost = 2500 \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index a23cf5bb4b..4240abe9e3 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -11,6 +11,12 @@ self_recharge = 1 use_external_power = 1 +/obj/item/weapon/gun/energy/taser/mounted/augment + self_recharge = 1 + use_external_power = 0 + use_organic_power = TRUE + canremove = FALSE + /obj/item/weapon/gun/energy/taser/mounted/cyborg name = "taser gun" charge_cost = 400 diff --git a/code/modules/projectiles/guns/magnetic/bore.dm b/code/modules/projectiles/guns/magnetic/bore.dm index e27fdc38d6..4cb86a6d31 100644 --- a/code/modules/projectiles/guns/magnetic/bore.dm +++ b/code/modules/projectiles/guns/magnetic/bore.dm @@ -86,7 +86,17 @@ user.visible_message("\The [user] slots \the [cell] into \the [src].") update_icon() return - + if(thing.is_crowbar()) + if(!manipulator) + to_chat(user, "\The [src] has no manipulator installed.") + return + manipulator.forceMove(get_turf(src)) + user.put_in_hands(manipulator) + user.visible_message("\The [user] levers \the [manipulator] from \the [src].") + playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) + manipulator = null + update_icon() + return if(thing.is_screwdriver()) if(!capacitor) to_chat(user, "\The [src] has no capacitor installed.") @@ -112,6 +122,20 @@ update_icon() return + if(istype(thing, /obj/item/weapon/stock_parts/manipulator)) + if(manipulator) + to_chat(user, "\The [src] already has \a [manipulator] installed.") + return + manipulator = thing + user.drop_from_inventory(manipulator) + manipulator.forceMove(src) + playsound(loc, 'sound/machines/click.ogg', 10,1) + mat_cost = initial(mat_cost) % (2*manipulator.rating) + user.visible_message("\The [user] slots \the [manipulator] into \the [src].") + update_icon() + return + + if(istype(thing, load_type)) loading = TRUE var/obj/item/stack/material/M = thing diff --git a/code/modules/projectiles/guns/magnetic/magnetic.dm b/code/modules/projectiles/guns/magnetic/magnetic.dm index 20e0ea8512..c327a2b51e 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic.dm @@ -10,6 +10,7 @@ var/obj/item/weapon/cell/cell // Currently installed powercell. var/obj/item/weapon/stock_parts/capacitor/capacitor // Installed capacitor. Higher rating == faster charge between shots. + var/obj/item/weapon/stock_parts/manipulator/manipulator // Installed manipulator. Mostly for Phoron Bore, higher rating == less mats consumed upon firing var/removable_components = TRUE // Whether or not the gun can be dismantled. var/gun_unreliable = 15 // Percentage chance of detonating in your hands. diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 85d8d5ecdf..ee04e27653 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -109,6 +109,8 @@ obj/item/weapon/gun/projectile/revolver/detective45/verb/rename_gun() options["H&K PT"] = "detective_panther" options["Vintage LeMat"] = "lemat_old" options["Webley MKVI "] = "webley" + options["Lombardi Buzzard"] = "detective_buzzard" + options["Constable Deluxe 2502"] = "detective_constable" var/choice = input(M,"Choose your sprite!","Resprite Gun") in options if(src && choice && !M.stat && in_range(M,src)) icon_state = options[choice] @@ -275,4 +277,4 @@ obj/item/weapon/gun/projectile/revolver/detective45/verb/rename_gun() desc = "A shiny Mosley Autococker automatic revolver, with black accents. Marketed as the 'Revolver for the Modern Era'. Uses .44 magnum rounds." fire_delay = 5.7 //Autorevolver. Also synced with the animation fire_anim = "mosley_fire" - origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2) \ No newline at end of file + origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 6c784b44ed..b3b70aa7af 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -44,6 +44,7 @@ var/tracer_type var/muzzle_type var/impact_type + var/datum/beam_components_cache/beam_components //Fancy hitscan lighting effects! var/hitscan_light_intensity = 1.5 @@ -84,8 +85,17 @@ var/accuracy = 0 var/dispersion = 0.0 + // Sub-munitions. Basically, multi-projectile shotgun, rather than pellets. + var/use_submunitions = FALSE + var/only_submunitions = FALSE // Will the projectile delete itself after firing the submunitions? + var/list/submunitions = list() // Assoc list of the paths of any submunitions, and how many they are. [projectilepath] = [projectilecount]. + var/submunition_spread_max = 30 // Divided by 10 to get the percentile dispersion. + var/submunition_spread_min = 5 // Above. + var/force_max_submunition_spread = FALSE // Do we just force the maximum? + var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? + var/damage = 10 - var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS are the only things that should be in here + var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. var/nodamage = 0 //Determines if the projectile will skip any damage inflictions @@ -352,10 +362,18 @@ /obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0) var/turf/curloc = get_turf(source) var/turf/targloc = get_turf(target) + + if(istype(source, /atom/movable)) + var/atom/movable/MT = source + if(MT.locs && MT.locs.len) // Multi tile! + for(var/turf/T in MT.locs) + if(get_dist(T, target) < get_turf(curloc)) + curloc = get_turf(T) + trajectory_ignore_forcemove = TRUE forceMove(get_turf(source)) trajectory_ignore_forcemove = FALSE - starting = get_turf(source) + starting = curloc original = target if(targloc || !params) yo = targloc.y - curloc.y @@ -421,7 +439,6 @@ if(hitscan) finalize_hitscan_and_generate_tracers() STOP_PROCESSING(SSprojectiles, src) - cleanup_beam_segments() qdel(trajectory) return ..() @@ -445,10 +462,11 @@ /obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE) if(!length(beam_segments)) return + beam_components = new if(tracer_type) var/tempref = "\ref[src]" for(var/datum/point/p in beam_segments) - generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) + generate_tracer_between_points(p, beam_segments[p], beam_components, tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) if(muzzle_type && duration > 0) var/datum/point/p = beam_segments[1] var/atom/movable/thing = new muzzle_type @@ -458,7 +476,7 @@ thing.transform = M thing.color = color thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color) - QDEL_IN(thing, duration) + beam_components.beam_components += thing if(impacting && impact_type && duration > 0) var/datum/point/p = beam_segments[beam_segments[beam_segments.len]] var/atom/movable/thing = new impact_type @@ -468,9 +486,8 @@ thing.transform = M thing.color = color thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color) - QDEL_IN(thing, duration) - if(cleanup) - cleanup_beam_segments() + beam_components.beam_components += thing + QDEL_IN(beam_components, duration) //Returns true if the target atom is on our current turf and above the right layer //If direct target is true it's the originally clicked target. @@ -647,6 +664,37 @@ if(get_turf(target) == get_turf(src)) direct_target = target + if(use_submunitions && submunitions.len) + var/temp_min_spread = 0 + if(force_max_submunition_spread) + temp_min_spread = submunition_spread_max + else + temp_min_spread = submunition_spread_min + + var/damage_override = null + + if(spread_submunition_damage) + damage_override = damage + if(nodamage) + damage_override = 0 + + var/projectile_count = 0 + + for(var/proj in submunitions) + projectile_count += submunitions[proj] + + damage_override = round(damage_override / max(1, projectile_count)) + + for(var/path in submunitions) + for(var/count = 1 to submunitions[path]) + var/obj/item/projectile/SM = new path(get_turf(loc)) + SM.shot_from = shot_from + SM.silenced = silenced + SM.dispersion = rand(temp_min_spread, submunition_spread_max) / 10 + if(!isnull(damage_override)) + SM.damage = damage_override + SM.launch_projectile(target, target_zone, user, params, angle_override) + preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) return fire(angle_override, direct_target) @@ -657,3 +705,45 @@ silenced = launcher.silenced return launch_projectile(target, target_zone, user, params, angle_override, forced_spread) + +/obj/item/projectile/proc/launch_projectile_from_turf(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + original = target + def_zone = check_zone(target_zone) + firer = user + var/direct_target + if(get_turf(target) == get_turf(src)) + direct_target = target + + if(use_submunitions && submunitions.len) + var/temp_min_spread = 0 + if(force_max_submunition_spread) + temp_min_spread = submunition_spread_max + else + temp_min_spread = submunition_spread_min + + var/damage_override = null + + if(spread_submunition_damage) + damage_override = damage + if(nodamage) + damage_override = 0 + + var/projectile_count = 0 + + for(var/proj in submunitions) + projectile_count += submunitions[proj] + + damage_override = round(damage_override / max(1, projectile_count)) + + for(var/path in submunitions) + for(var/count = 1 to submunitions[path]) + var/obj/item/projectile/SM = new path(get_turf(loc)) + SM.shot_from = shot_from + SM.silenced = silenced + SM.dispersion = rand(temp_min_spread, submunition_spread_max) / 10 + if(!isnull(damage_override)) + SM.damage = damage_override + SM.launch_projectile_from_turf(target, target_zone, user, params, angle_override) + + preparePixelProjectile(target, get_turf(src), params, forced_spread) + return fire(angle_override, direct_target) diff --git a/code/modules/projectiles/projectile/arc.dm b/code/modules/projectiles/projectile/arc.dm index 97069db1fb..1f19dc0242 100644 --- a/code/modules/projectiles/projectile/arc.dm +++ b/code/modules/projectiles/projectile/arc.dm @@ -162,8 +162,9 @@ /obj/item/projectile/arc/radioactive name = "radiation blast" icon_state = "green_pellet" - icon_scale = 2 + icon_scale_x = 2 + icon_scale_y = 2 var/rad_power = 50 /obj/item/projectile/arc/radioactive/on_impact(turf/T) - radiation_repository.radiate(T, rad_power) + SSradiation.radiate(T, rad_power) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 9073ead831..ec2670c61d 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -63,7 +63,7 @@ icon_state = "emitter" fire_sound = 'sound/weapons/emitter.ogg' light_color = "#00CC33" - excavation_amount = 70 // 3 shots to mine a turf + excavation_amount = 140 // 2 shots to dig a standard rock turf. Superior due to being a mounted tool beam, to make it actually viable. muzzle_type = /obj/effect/projectile/muzzle/emitter tracer_type = /obj/effect/projectile/tracer/emitter @@ -89,6 +89,7 @@ /obj/item/projectile/beam/cyan name = "cyan beam" icon_state = "cyan" + fire_sound = 'sound/weapons/eluger.ogg' damage = 40 light_color = "#00C6FF" @@ -125,62 +126,52 @@ tracer_type = /obj/effect/projectile/tracer/emitter impact_type = /obj/effect/projectile/impact/emitter -/obj/item/projectile/beam/lastertag/blue +/obj/item/projectile/beam/lasertag name = "lasertag beam" - icon_state = "bluelaser" damage = 0 + eyeblur = 0 no_attack_log = 1 damage_type = BURN check_armour = "laser" - light_color = "#0066FF" combustion = FALSE +/obj/item/projectile/beam/lasertag/blue + icon_state = "bluelaser" + light_color = "#0066FF" + muzzle_type = /obj/effect/projectile/muzzle/laser_blue tracer_type = /obj/effect/projectile/tracer/laser_blue impact_type = /obj/effect/projectile/impact/laser_blue -/obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0) - if(istype(target, /mob/living/carbon/human)) +/obj/item/projectile/beam/lasertag/blue/on_hit(var/atom/target, var/blocked = 0) + if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) M.Weaken(5) return 1 -/obj/item/projectile/beam/lastertag/red - name = "lasertag beam" +/obj/item/projectile/beam/lasertag/red icon_state = "laser" - damage = 0 - no_attack_log = 1 - damage_type = BURN - check_armour = "laser" light_color = "#FF0D00" - combustion = FALSE - -/obj/item/projectile/beam/lastertag/red/on_hit(var/atom/target, var/blocked = 0) - if(istype(target, /mob/living/carbon/human)) +/obj/item/projectile/beam/lasertag/red/on_hit(var/atom/target, var/blocked = 0) + if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) M.Weaken(5) return 1 -/obj/item/projectile/beam/lastertag/omni//A laser tag bolt that stuns EVERYONE - name = "lasertag beam" +/obj/item/projectile/beam/lasertag/omni//A laser tag bolt that stuns EVERYONE icon_state = "omnilaser" - damage = 0 - damage_type = BURN - check_armour = "laser" light_color = "#00C6FF" - combustion = FALSE - muzzle_type = /obj/effect/projectile/muzzle/laser_omni tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0) - if(istype(target, /mob/living/carbon/human)) +/obj/item/projectile/beam/lasertag/omni/on_hit(var/atom/target, var/blocked = 0) + if(ishuman(target)) var/mob/living/carbon/human/M = target if((istype(M.wear_suit, /obj/item/clothing/suit/bluetag))||(istype(M.wear_suit, /obj/item/clothing/suit/redtag))) M.Weaken(5) @@ -222,4 +213,17 @@ /obj/item/projectile/beam/stun/med name = "stun beam" icon_state = "stun" - agony = 30 \ No newline at end of file + agony = 30 + +/obj/item/projectile/beam/shock + name = "shock beam" + icon_state = "lightning" + damage_type = ELECTROCUTE + + muzzle_type = /obj/effect/projectile/muzzle/lightning + tracer_type = /obj/effect/projectile/tracer/lightning + impact_type = /obj/effect/projectile/impact/lightning + + damage = 30 + agony = 15 + eyeblur = 2 diff --git a/code/modules/projectiles/projectile/beams_vr.dm b/code/modules/projectiles/projectile/beams_vr.dm index b6ec5cbe0b..8930260e9e 100644 --- a/code/modules/projectiles/projectile/beams_vr.dm +++ b/code/modules/projectiles/projectile/beams_vr.dm @@ -41,3 +41,37 @@ muzzle_type = /obj/effect/projectile/muzzle/laser_blue tracer_type = /obj/effect/projectile/tracer/laser_blue impact_type = /obj/effect/projectile/impact/laser_blue + +/obj/item/projectile/beam/medigun + name = "healing beam" + icon_state = "healbeam" + damage = 0 //stops it damaging walls + nodamage = TRUE + no_attack_log = TRUE + damage_type = BURN + check_armour = "laser" + light_color = "#80F5FF" + + combustion = FALSE + + muzzle_type = /obj/effect/projectile/muzzle/medigun + tracer_type = /obj/effect/projectile/tracer/medigun + impact_type = /obj/effect/projectile/impact/medigun + +/obj/item/projectile/beam/medigun/on_hit(var/atom/target, var/blocked = 0) + if(istype(target, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = target + if(M.health < M.maxHealth) + var/obj/effect/overlay/pulse = new /obj/effect/overlay(get_turf(M)) + pulse.icon = 'icons/effects/effects.dmi' + pulse.icon_state = "heal" + pulse.name = "heal" + pulse.anchored = 1 + spawn(20) + qdel(pulse) + to_chat(target, "As the beam strikes you, your injuries close up!") + M.adjustBruteLoss(-15) + M.adjustFireLoss(-15) + M.adjustToxLoss(-5) + M.adjustOxyLoss(-5) + return 1 \ No newline at end of file diff --git a/code/modules/projectiles/projectile/blob.dm b/code/modules/projectiles/projectile/blob.dm index 926c78a0a0..7ebc5cdfad 100644 --- a/code/modules/projectiles/projectile/blob.dm +++ b/code/modules/projectiles/projectile/blob.dm @@ -26,13 +26,14 @@ ..() /obj/item/projectile/energy/blob/on_impact(var/atom/A) - var/turf/location = get_turf(src) - var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem - S.attach(location) - S.set_up(reagents, splatter_volume, 0, location) - playsound(location, 'sound/effects/slime_squish.ogg', 30, 1, -3) - spawn(0) - S.start() + if(splatter) + var/turf/location = get_turf(src) + var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem + S.attach(location) + S.set_up(reagents, splatter_volume, 0, location) + playsound(location, 'sound/effects/slime_squish.ogg', 30, 1, -3) + spawn(0) + S.start() ..() /obj/item/projectile/energy/blob/proc/ready_chemicals() @@ -61,3 +62,11 @@ splatter = TRUE flammability = 0.25 my_chems = list("fuel", "mold") + +/obj/item/projectile/energy/blob/freezing + my_chems = list("frostoil") + modifier_type_to_apply = /datum/modifier/chilled + modifier_duration = 1 MINUTE + +/obj/item/projectile/energy/blob/freezing/splattering + splatter = TRUE diff --git a/code/modules/projectiles/projectile/scatter.dm b/code/modules/projectiles/projectile/scatter.dm new file mode 100644 index 0000000000..0aa6ad5719 --- /dev/null +++ b/code/modules/projectiles/projectile/scatter.dm @@ -0,0 +1,62 @@ + +/* + * Home of the purely submunition projectiles. + */ + +/obj/item/projectile/scatter + name = "scatter projectile" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bullet" + density = FALSE + anchored = TRUE + unacidable = TRUE + pass_flags = PASSTABLE + mouse_opacity = 0 + + use_submunitions = TRUE + + damage = 8 + spread_submunition_damage = TRUE + only_submunitions = TRUE + range = 0 // Immediately deletes itself after firing, as its only job is to fire other projectiles. + + submunition_spread_max = 30 + submunition_spread_min = 2 + + submunitions = list( + /obj/item/projectile/bullet/pellet/shotgun/flak = 3 + ) + +/obj/item/projectile/scatter/laser + damage = 40 + + submunition_spread_max = 40 + submunition_spread_min = 10 + + submunitions = list( + /obj/item/projectile/beam/prismatic = 4 + ) + +/obj/item/projectile/beam/prismatic + name = "prismatic beam" + icon_state = "omnilaser" + damage = 10 + damage_type = BURN + check_armour = "laser" + light_color = "#00C6FF" + + stutter = 2 + + muzzle_type = /obj/effect/projectile/muzzle/laser_omni + tracer_type = /obj/effect/projectile/tracer/laser_omni + impact_type = /obj/effect/projectile/impact/laser_omni + +/obj/item/projectile/scatter/ion + damage = 20 + + submunition_spread_max = 40 + submunition_spread_min = 10 + + submunitions = list( + /obj/item/projectile/bullet/shotgun/ion = 3 + ) diff --git a/code/modules/projectiles/targeting/targeting_gun.dm b/code/modules/projectiles/targeting/targeting_gun.dm index e2ef466255..3b80658226 100644 --- a/code/modules/projectiles/targeting/targeting_gun.dm +++ b/code/modules/projectiles/targeting/targeting_gun.dm @@ -17,5 +17,7 @@ user.face_atom(A) if(ismob(A) && user.aiming) user.aiming.aim_at(A, src) + if(!isliving(A)) + return 0 return 1 return 0 \ No newline at end of file diff --git a/code/modules/radiation/radiation.dm b/code/modules/radiation/radiation.dm new file mode 100644 index 0000000000..d913ad7cab --- /dev/null +++ b/code/modules/radiation/radiation.dm @@ -0,0 +1,59 @@ +// Describes a point source of radiation. Created either in response to a pulse of radiation, or over an irradiated atom. +// Sources will decay over time, unless something is renewing their power! +/datum/radiation_source + var/turf/source_turf // Location of the radiation source. + var/rad_power // Strength of the radiation being emitted. + var/decay = TRUE // True for automatic decay. False if owner promises to handle it (i.e. supermatter) + var/respect_maint = FALSE // True for not affecting RAD_SHIELDED areas. + var/flat = FALSE // True for power falloff with distance. + var/range // Cached maximum range, used for quick checks against mobs. + +/datum/radiation_source/Destroy() + SSradiation.sources -= src + if(SSradiation.sources_assoc[src.source_turf] == src) + SSradiation.sources_assoc -= src.source_turf + src.source_turf = null + . = ..() + +/datum/radiation_source/proc/update_rad_power(var/new_power = null) + if(new_power == null || new_power == rad_power) + return // No change + else if(new_power <= config.radiation_lower_limit) + qdel(src) // Decayed to nothing + else + rad_power = new_power + if(!flat) + range = min(round(sqrt(rad_power / config.radiation_lower_limit)), 31) // R = rad_power / dist**2 - Solve for dist + +/turf + var/cached_rad_resistance = 0 + +/turf/proc/calc_rad_resistance() + cached_rad_resistance = 0 + for(var/obj/O in src.contents) + if(O.rad_resistance) //Override + cached_rad_resistance += O.rad_resistance + + else if(O.density) //So open doors don't get counted + var/material/M = O.get_material() + if(!M) continue + cached_rad_resistance += M.weight + M.radiation_resistance + // Looks like storing the contents length is meant to be a basic check if the cache is stale due to items enter/exiting. Better than nothing so I'm leaving it as is. ~Leshana + SSradiation.resistance_cache[src] = (length(contents) + 1) + +/turf/simulated/wall/calc_rad_resistance() + SSradiation.resistance_cache[src] = (length(contents) + 1) + cached_rad_resistance = (density ? material.weight + material.radiation_resistance : 0) + +/obj + var/rad_resistance = 0 // Allow overriding rad resistance + +// If people expand the system, this may be useful. Here as a placeholder until then +/atom/proc/rad_act(var/severity) + return 1 + +/mob/living/rad_act(var/severity) + if(severity && !isbelly(loc)) //eaten mobs are made immune to radiation //VOREStation Edit + src.apply_effect(severity, IRRADIATE, src.getarmor(null, "rad")) + for(var/atom/I in src) + I.rad_act(severity) \ No newline at end of file diff --git a/code/modules/random_map/automata/caves.dm b/code/modules/random_map/automata/caves.dm index 264fe8bc44..58e61045b3 100644 --- a/code/modules/random_map/automata/caves.dm +++ b/code/modules/random_map/automata/caves.dm @@ -47,7 +47,7 @@ if(!current_cell) return 0 var/turf/simulated/mineral/T = locate((origin_x-1)+x,(origin_y-1)+y,origin_z) - if(istype(T) && !T.ignore_mapgen) + if(istype(T) && !T.ignore_mapgen && !T.ignore_cavegen) //VOREStation Edit: ignore cavegen if(map[current_cell] == FLOOR_CHAR) T.make_floor() //VOREStation Edit - Don't make cracked sand on surface map, jerk. //if(prob(90)) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 4d12abddfe..467d7c37a9 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -88,8 +88,8 @@ reaction_occurred = FALSE for(var/i in reagent_list) var/datum/reagent/R = i - if(SSchemistry.chemical_reactions_by_reagent[R.id]) //VOREStation Edit: unruntiming chems - eligible_reactions |= SSchemistry.chemical_reactions_by_reagent[R.id] //VOREStation Edit: unruntiming chems + if(SSchemistry.chemical_reactions_by_reagent[R.id]) + eligible_reactions |= SSchemistry.chemical_reactions_by_reagent[R.id] for(var/i in eligible_reactions) var/datum/chemical_reaction/C = i diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index ee77c5e79e..a481cfaf33 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -127,7 +127,7 @@ data["pillSprite"] = pillsprite data["bottleSprite"] = bottlesprite - var/P[20] //how many pill sprites there are. Sprites are taken from chemical.dmi and can be found in nano/images/pill.png + var/P[24] //how many pill sprites there are. Sprites are taken from chemical.dmi and can be found in nano/images/pill.png for(var/i = 1 to P.len) P[i] = i data["pillSpritesAmount"] = P @@ -238,7 +238,7 @@ else pill_cube = "pill" - var/name = sanitizeSafe(input(usr,"Name:","Name your [pill_cube]!","[reagents.get_master_reagent_name()] ([amount_per_pill] units)") as null|text, MAX_NAME_LEN) + var/name = sanitizeSafe(input(usr,"Name:","Name your [pill_cube]!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)") as null|text, MAX_NAME_LEN) if(!name) //Blank name (sanitized to nothing, or left empty) or cancel return @@ -257,6 +257,10 @@ else //If condi is on P.icon_state = "bouilloncube"//Reskinned monkey cube P.desc = "A dissolvable cube." + + if(P.icon_state in list("pill1", "pill2", "pill3", "pill4")) // if using greyscale, take colour from reagent + P.color = reagents.get_color() + reagents.trans_to_obj(P,amount_per_pill) if(src.loaded_pill_bottle) if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space) @@ -277,6 +281,28 @@ var/obj/item/weapon/reagent_containers/food/condiment/P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc) reagents.trans_to_obj(P,50) + else if (href_list["createpatch"]) + if(reagents.total_volume < 1) //Sanity checking. + return + + var/name = sanitizeSafe(input(usr,"Name:","Name your patch!","[reagents.get_master_reagent_name()] ([round(reagents.total_volume)]u)") as null|text, MAX_NAME_LEN) + + if(!name) //Blank name (sanitized to nothing, or left empty) or cancel + return + + if(reagents.total_volume < 1) //Sanity checking. + return + var/obj/item/weapon/reagent_containers/pill/patch/P = new/obj/item/weapon/reagent_containers/pill/patch(src.loc) + if(!name) name = reagents.get_master_reagent_name() + P.name = "[name] patch" + P.pixel_x = rand(-7, 7) //random position + P.pixel_y = rand(-7, 7) + + reagents.trans_to_obj(P, 60) + if(src.loaded_pill_bottle) + if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space) + P.loc = loaded_pill_bottle + else if(href_list["pill_sprite"]) pillsprite = href_list["pill_sprite"] else if(href_list["bottle_sprite"]) diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index 8ad8b6486d..0b010dc4f6 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -20,6 +20,7 @@ var/max_dose = 0 var/overdose = 0 //Amount at which overdose starts var/overdose_mod = 2 //Modifier to overdose damage + var/can_overdose_touch = FALSE // Can the chemical OD when processing on touch? var/scannable = 0 // Shows up on health analyzers. var/affects_dead = 0 var/cup_icon_state = null @@ -60,19 +61,25 @@ var/datum/reagents/metabolism/active_metab = location var/removed = metabolism + var/ingest_rem_mult = 1 + var/ingest_abs_mult = 1 + if(!mrate_static == TRUE) // Modifiers for(var/datum/modifier/mod in M.modifiers) if(!isnull(mod.metabolism_percent)) removed *= mod.metabolism_percent + ingest_rem_mult *= mod.metabolism_percent // Species removed *= M.species.metabolic_rate + ingest_rem_mult *= M.species.metabolic_rate // Metabolism removed *= active_metab.metabolism_speed + ingest_rem_mult *= active_metab.metabolism_speed if(ishuman(M)) var/mob/living/carbon/human/H = M - if(H.species.has_organ[O_HEART]) + if(H.species.has_organ[O_HEART] && (active_metab.metabolism_class == CHEM_BLOOD)) var/obj/item/organ/internal/heart/Pump = H.internal_organs_by_name[O_HEART] if(!Pump) removed *= 0.1 @@ -80,14 +87,31 @@ removed *= 0.8 else // Otherwise, chemicals process as per percentage of your current pulse, or, if you have no pulse but are alive, by a miniscule amount. removed *= max(0.1, H.pulse / Pump.standard_pulse_level) + + if(H.species.has_organ[O_STOMACH] && (active_metab.metabolism_class == CHEM_INGEST)) + var/obj/item/organ/internal/stomach/Chamber = H.internal_organs_by_name[O_STOMACH] + if(Chamber) + ingest_rem_mult *= max(0.1, 1 - (Chamber.damage / Chamber.max_damage)) + else + ingest_rem_mult = 0.1 + + if(H.species.has_organ[O_INTESTINE] && (active_metab.metabolism_class == CHEM_INGEST)) + var/obj/item/organ/internal/intestine/Tube = H.internal_organs_by_name[O_INTESTINE] + if(Tube) + ingest_abs_mult *= max(0.1, 1 - (Tube.damage / Tube.max_damage)) + else + ingest_abs_mult = 0.1 + if(filtered_organs && filtered_organs.len) for(var/organ_tag in filtered_organs) var/obj/item/organ/internal/O = H.internal_organs_by_name[organ_tag] if(O && !O.is_broken() && prob(max(0, O.max_damage - O.damage))) removed *= 0.8 + if(active_metab.metabolism_class == CHEM_INGEST) + ingest_rem_mult *= 0.8 if(ingest_met && (active_metab.metabolism_class == CHEM_INGEST)) - removed = ingest_met + removed = ingest_met * ingest_rem_mult if(touch_met && (active_metab.metabolism_class == CHEM_TOUCH)) removed = touch_met removed = min(removed, volume) @@ -98,10 +122,10 @@ if(CHEM_BLOOD) affect_blood(M, alien, removed) if(CHEM_INGEST) - affect_ingest(M, alien, removed) + affect_ingest(M, alien, removed * ingest_abs_mult) if(CHEM_TOUCH) affect_touch(M, alien, removed) - if(overdose && (volume > overdose) && (active_metab.metabolism_class != CHEM_TOUCH)) + if(overdose && (volume > overdose) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch)) overdose(M, alien, removed) remove_self(removed) return diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm index f96d53aaac..0171551226 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm @@ -7,6 +7,15 @@ reagent_state = SOLID color = "#A8A8A8" +/datum/reagent/calcium + name = "Calcium" + id = "calcium" + description = "A chemical element, the building block of bones." + taste_description = "metallic chalk" // Apparently, calcium tastes like calcium. + taste_mult = 1.3 + reagent_state = SOLID + color = "#e9e6e4" + /datum/reagent/carbon name = "Carbon" id = "carbon" diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index 6528826ac7..48afe19b7e 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -92,6 +92,12 @@ taste_description = "egg" color = "#FFFFAA" +/datum/reagent/nutriment/protein/murk + name = "murkfin protein" + id = "murk_protein" + taste_description = "mud" + color = "#664330" + /datum/reagent/nutriment/honey name = "Honey" id = "honey" @@ -121,6 +127,14 @@ M.sleeping = max(M.sleeping, 20) M.drowsyness = max(M.drowsyness, 60) +/datum/reagent/nutriment/mayo + name = "mayonnaise" + id = "mayo" + description = "A thick, bitter sauce." + taste_description = "unmistakably mayonnaise" + nutriment_factor = 10 + color = "#FFFFFF" + /datum/reagent/nutriment/flour name = "Flour" id = "flour" @@ -134,6 +148,24 @@ if(!istype(T, /turf/space)) new /obj/effect/decal/cleanable/flour(T) +/datum/reagent/nutriment/coffee + name = "Coffee Powder" + id = "coffeepowder" + description = "A bitter powder made by grinding coffee beans." + taste_description = "bitterness" + taste_mult = 1.3 + nutriment_factor = 1 + color = "#482000" + +/datum/reagent/nutriment/tea + name = "Tea Powder" + id = "teapowder" + description = "A dark, tart powder made from black tea leaves." + taste_description = "tartness" + taste_mult = 1.3 + nutriment_factor = 1 + color = "#101000" + /datum/reagent/nutriment/coco name = "Coco Powder" id = "coco" @@ -144,6 +176,41 @@ nutriment_factor = 5 color = "#302000" +/datum/reagent/nutriment/instantjuice + name = "Juice Powder" + id = "instantjuice" + description = "Dehydrated, powdered juice of some kind." + taste_mult = 1.3 + nutriment_factor = 1 + +/datum/reagent/nutriment/instantjuice/grape + name = "Grape Juice Powder" + id = "instantgrape" + description = "Dehydrated, powdered grape juice." + taste_description = "dry grapes" + color = "#863333" + +/datum/reagent/nutriment/instantjuice/orange + name = "Orange Juice Powder" + id = "instantorange" + description = "Dehydrated, powdered orange juice." + taste_description = "dry oranges" + color = "#e78108" + +/datum/reagent/nutriment/instantjuice/watermelon + name = "Watermelon Juice Powder" + id = "instantwatermelon" + description = "Dehydrated, powdered watermelon juice." + taste_description = "dry sweet watermelon" + color = "#b83333" + +/datum/reagent/nutriment/instantjuice/apple + name = "Apple Juice Powder" + id = "instantapple" + description = "Dehydrated, powdered apple juice." + taste_description = "dry sweet apples" + color = "#c07c40" + /datum/reagent/nutriment/soysauce name = "Soysauce" id = "soysauce" @@ -208,6 +275,76 @@ if(volume >= 3) T.wet_floor() +/datum/reagent/nutriment/peanutoil + name = "Peanut Oil" + id = "peanutoil" + description = "An oil derived from various types of nuts." + taste_description = "nuts" + taste_mult = 0.3 + reagent_state = LIQUID + nutriment_factor = 15 + color = "#4F3500" + +/datum/reagent/nutriment/peanutoil/touch_turf(var/turf/simulated/T) + if(!istype(T)) + return + + var/hotspot = (locate(/obj/fire) in T) + if(hotspot && !istype(T, /turf/space)) + var/datum/gas_mixture/lowertemp = T.remove_air(T:air:total_moles) + lowertemp.temperature = max(min(lowertemp.temperature-2000, lowertemp.temperature / 2), 0) + lowertemp.react() + T.assume_air(lowertemp) + qdel(hotspot) + + if(volume >= 5) + T.wet_floor() + +/datum/reagent/nutriment/peanutbutter + name = "Peanut Butter" + id = "peanutbutter" + description = "A butter derived from various types of nuts." + taste_description = "peanuts" + taste_mult = 0.5 + reagent_state = LIQUID + nutriment_factor = 30 + color = "#4F3500" + +/datum/reagent/nutriment/vanilla + name = "Vanilla Extract" + id = "vanilla" + description = "Vanilla extract. Tastes suspiciously like boring ice-cream." + taste_description = "vanilla" + taste_mult = 5 + reagent_state = LIQUID + nutriment_factor = 2 + color = "#0F0A00" + +/datum/reagent/nutriment/durian + name = "Durian Paste" + id = "durianpaste" + description = "A strangely sweet and savory paste." + taste_description = "sweet and savory" + color = "#757631" + + glass_name = "durian paste" + glass_desc = "Durian paste. It smells horrific." + +/datum/reagent/nutriment/durian/touch_mob(var/mob/M, var/amount) + if(iscarbon(M) && !M.isSynthetic()) + var/message = pick("Oh god, it smells disgusting here.", "What is that stench?", "That's an awful odor.") + to_chat(M,"[message]") + if(prob(CLAMP(amount, 5, 90))) + var/mob/living/L = M + L.vomit() + return ..() + +/datum/reagent/nutriment/durian/touch_turf(var/turf/T, var/amount) + if(istype(T)) + var/obj/effect/decal/cleanable/chemcoating/C = new /obj/effect/decal/cleanable/chemcoating(T) + C.reagents.add_reagent(id, amount) + return ..() + /datum/reagent/nutriment/virus_food name = "Virus Food" id = "virusfood" @@ -553,6 +690,16 @@ glass_name = "berry juice" glass_desc = "Berry juice. Or maybe it's jam. Who cares?" +/datum/reagent/drink/juice/pineapple + name = "Pineapple Juice" + id = "pineapplejuice" + description = "A sour but refreshing juice from a pineapple." + taste_description = "pineapple" + color = "#C3AF00" + + glass_name = "pineapple juice" + glass_desc = "Pineapple juice. Or maybe it's spineapple. Who cares?" + /datum/reagent/drink/juice/carrot name = "Carrot juice" id = "carrotjuice" @@ -1074,6 +1221,42 @@ glass_desc = "Oh the nostalgia..." glass_special = list(DRINK_FIZZ) +/datum/reagent/drink/soda/melonade + name = "Melonade" + id = "melonade" + description = "Oh the.. nostalgia?" + taste_description = "watermelon" + color = "#FFB3BB" + adj_temp = -5 + + glass_name = "melonade" + glass_desc = "Oh the.. nostalgia?" + glass_special = list(DRINK_FIZZ) + +/datum/reagent/drink/soda/appleade + name = "Appleade" + id = "appleade" + description = "Applejuice, improved." + taste_description = "apples" + color = "#FFD1B3" + adj_temp = -5 + + glass_name = "appleade" + glass_desc = "Applejuice, improved." + glass_special = list(DRINK_FIZZ) + +/datum/reagent/drink/soda/pineappleade + name = "Pineappleade" + id = "pineappleade" + description = "Spineapple, juiced up." + taste_description = "sweet`n`sour pineapples" + color = "#FFFF00" + adj_temp = -5 + + glass_name = "pineappleade" + glass_desc = "Spineapple, juiced up." + glass_special = list(DRINK_FIZZ) + /datum/reagent/drink/soda/kiraspecial name = "Kira Special" id = "kiraspecial" @@ -1169,6 +1352,16 @@ /datum/reagent/drink/milkshake/coffeeshake/overdose(var/mob/living/carbon/M, var/alien) M.make_jittery(5) +/datum/reagent/drink/milkshake/peanutshake + name = "Peanut Milkshake" + id = "peanutmilkshake" + description = "Savory cream in an ice-cold stature." + taste_description = "cold peanuts and cream" + color = "#8e6f44" + + glass_name = "Peanut Milkshake" + glass_desc = "Savory cream in an ice-cold stature." + /datum/reagent/drink/rewriter name = "Rewriter" id = "rewriter" @@ -1211,6 +1404,7 @@ description = "Made in the modern day with proper pomegranate substitute. Who uses real fruit, anyways?" taste_description = "100% pure pomegranate" color = "#FF004F" + water_based = FALSE glass_name = "grenadine syrup" glass_desc = "Sweet and tangy, a bar syrup used to add color or flavor to drinks." @@ -1279,7 +1473,6 @@ glass_desc = "A tangy substance made of 0.5% natural citrus!" glass_special = list(DRINK_FIZZ) - /datum/reagent/drink/soda/gingerale name = "Ginger Ale" id = "gingerale" @@ -1503,6 +1696,18 @@ glass_desc = "A concoction that should probably be in an engine, rather than your stomach." glass_icon = DRINK_ICON_NOISY +/datum/reagent/drink/slimeslammer + name = "Slick Slimes Slammer" + id = "slimeslammer" + description = "A viscous, but savory, ooze." + taste_description = "peanuts`n`slime" + color = "#93604D" + water_based = FALSE + + glass_name = "Slick Slime Slammer" + glass_desc = "A concoction that should probably be in an engine, rather than your stomach. Still." + glass_icon = DRINK_ICON_NOISY + /datum/reagent/drink/eggnog name = "Eggnog" id = "eggnog" @@ -1569,6 +1774,73 @@ if(D.water_based) M.adjustToxLoss(removed * -2) +/datum/reagent/drink/mojito + name = "Mojito" + id = "virginmojito" + description = "Mint, bubbly water, and citrus, made for sailing." + taste_description = "mint and lime" + color = "#FFF7B3" + + glass_name = "mojito" + glass_desc = "Mint, bubbly water, and citrus, made for sailing." + glass_special = list(DRINK_FIZZ) + +/datum/reagent/drink/sexonthebeach + name = "Virgin Sex On The Beach" + id = "virginsexonthebeach" + description = "A secret combination of orange juice and pomegranate." + taste_description = "60% orange juice, 40% pomegranate" + color = "#7051E3" + + glass_name = "sex on the beach" + glass_desc = "A secret combination of orange juice and pomegranate." + +/datum/reagent/drink/driverspunch + name = "Driver's Punch" + id = "driverspunch" + description = "A fruity punch!" + taste_description = "sharp, sour apples" + color = "#D2BA6E" + + glass_name = "driver`s punch" + glass_desc = "A fruity punch!" + glass_special = list(DRINK_FIZZ) + +/datum/reagent/drink/berrycordial + name = "Berry Cordial" + id = "berrycordial" + description = "How berry cordial of you." + taste_description = "sweet chivalry" + color = "#D26EB8" + + glass_name = "berry cordial" + glass_desc = "How berry cordial of you." + glass_icon = DRINK_ICON_NOISY + +/datum/reagent/drink/tropicalfizz + name = "Tropical Fizz" + id = "tropicalfizz" + description = "One sip and you're in the bahamas." + taste_description = "tropical" + color = "#69375C" + + glass_name = "tropical fizz" + glass_desc = "One sip and you're in the bahamas." + glass_icon = DRINK_ICON_NOISY + glass_special = list(DRINK_FIZZ) + +/datum/reagent/drink/fauxfizz + name = "Faux Fizz" + id = "fauxfizz" + description = "One sip and you're in the bahamas... maybe." + taste_description = "slightly tropical" + color = "#69375C" + + glass_name = "tropical fizz" + glass_desc = "One sip and you're in the bahamas... maybe." + glass_icon = DRINK_ICON_NOISY + glass_special = list(DRINK_FIZZ) + /* Alcohol */ // Basic @@ -1656,6 +1928,17 @@ return M.dizziness +=5 +/datum/reagent/ethanol/firepunch + name = "Fire Punch" + id = "firepunch" + description = "Yo ho ho and a jar of honey." + taste_description = "sharp butterscotch" + color = "#ECB633" + strength = 7 + + glass_name = "fire punch" + glass_desc = "Yo ho ho and a jar of honey." + /datum/reagent/ethanol/gin name = "Gin" id = "gin" @@ -1723,6 +2006,18 @@ glass_name = "melon liquor" glass_desc = "A relatively sweet and fruity 46 proof liquor." +/datum/reagent/ethanol/melonspritzer + name = "Melon Spritzer" + id = "melonspritzer" + description = "Melons: Citrus style." + taste_description = "sour melon" + color = "#934D5D" + strength = 10 + + glass_name = "melon spritzer" + glass_desc = "Melons: Citrus style." + glass_special = list(DRINK_FIZZ) + /datum/reagent/ethanol/rum name = "Rum" id = "rum" @@ -1746,6 +2041,17 @@ glass_name = "sake" glass_desc = "A glass of sake." +/datum/reagent/ethanol/sexonthebeach + name = "Sex On The Beach" + id = "sexonthebeach" + description = "A concoction of vodka and a secret combination of orange juice and pomegranate." + taste_description = "60% orange juice, 40% pomegranate, 100% alcohol" + color = "#7051E3" + strength = 15 + + glass_name = "sex on the beach" + glass_desc = "A concoction of vodka and a secret combination of orange juice and pomegranate." + /datum/reagent/ethanol/tequila name = "Tequila" id = "tequilla" @@ -1848,6 +2154,7 @@ glass_name = "cider" glass_desc = "The second most Irish drink." + glass_special = list(DRINK_FIZZ) // Cocktails @@ -3201,4 +3508,4 @@ strength = 9 glass_name = "fusionnaire" - glass_desc = "A relatively new cocktail, mostly served in the bars of NanoTrasen owned stations." \ No newline at end of file + glass_desc = "A relatively new cocktail, mostly served in the bars of NanoTrasen owned stations." diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index e2724f0647..ac51ab7168 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -16,6 +16,29 @@ M.add_chemical_effect(CE_STABLE, 15) M.add_chemical_effect(CE_PAINKILLER, 10) +/datum/reagent/inaprovaline/topical + name = "Inaprovalaze" + id = "inaprovalaze" + description = "Inaprovalaze is a topical variant of Inaprovaline." + taste_description = "bitterness" + reagent_state = LIQUID + color = "#00BFFF" + overdose = REAGENTS_OVERDOSE * 2 + metabolism = REM * 0.5 + scannable = 1 + touch_met = REM * 0.75 + can_overdose_touch = TRUE + +/datum/reagent/inaprovaline/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + ..() + M.adjustToxLoss(2 * removed) + +/datum/reagent/inaprovaline/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + M.add_chemical_effect(CE_STABLE, 20) + M.add_chemical_effect(CE_PAINKILLER, 12) + /datum/reagent/bicaridine name = "Bicaridine" id = "bicaridine" @@ -51,6 +74,53 @@ if(W.damage <= 0) O.wounds -= W +/datum/reagent/bicaridine/topical + name = "Bicaridaze" + id = "bicaridaze" + description = "Bicaridaze is a topical variant of the chemical Bicaridine." + taste_description = "bitterness" + taste_mult = 3 + reagent_state = LIQUID + color = "#BF0000" + overdose = REAGENTS_OVERDOSE * 0.75 + scannable = 1 + touch_met = REM * 0.75 + can_overdose_touch = TRUE + +/datum/reagent/bicaridine/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + ..(M, alien, removed * chem_effective) + M.adjustToxLoss(2 * removed) + +/datum/reagent/bicaridine/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + M.heal_organ_damage(6 * removed * chem_effective, 0) + +/datum/reagent/calciumcarbonate + name = "calcium carbonate" + id = "calciumcarbonate" + description = "Calcium carbonate is a calcium salt commonly used as an antacid." + taste_description = "chalk" + reagent_state = SOLID + color = "#eae6e3" + overdose = REAGENTS_OVERDOSE * 0.8 + metabolism = REM * 0.4 + scannable = 1 + +/datum/reagent/calciumcarbonate/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) // Why would you inject this. + if(alien != IS_DIONA) + M.adjustToxLoss(3 * removed) + +/datum/reagent/calciumcarbonate/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + M.add_chemical_effect(CE_ANTACID, 3) + /datum/reagent/kelotane name = "Kelotane" id = "kelotane" @@ -87,6 +157,34 @@ if(alien != IS_DIONA) M.heal_organ_damage(0, 8 * removed * chem_effective) //VOREStation edit +/datum/reagent/dermaline/topical + name = "Dermalaze" + id = "dermalaze" + description = "Dermalaze is a topical variant of the chemical Dermaline." + taste_description = "bitterness" + taste_mult = 1.5 + reagent_state = LIQUID + color = "#FF8000" + overdose = REAGENTS_OVERDOSE * 0.4 + scannable = 1 + touch_met = REM * 0.75 + can_overdose_touch = TRUE + +/datum/reagent/dermaline/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + ..(M, alien, removed * chem_effective) + M.adjustToxLoss(2 * removed) + +/datum/reagent/dermaline/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + M.heal_organ_damage(0, 12 * removed * chem_effective) + /datum/reagent/dylovene name = "Dylovene" id = "anti_toxin" @@ -203,6 +301,46 @@ M.heal_organ_damage(1.5 * removed, 1.5 * removed * chem_effective) M.adjustToxLoss(-1.5 * removed * chem_effective) +/datum/reagent/tricordrazine/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + affect_blood(M, alien, removed * 0.4) + +/datum/reagent/tricorlidaze + name = "Tricorlidaze" + id = "tricorlidaze" + description = "Tricorlidaze is a topical gel produced with tricordrazine and sterilizine." + taste_description = "bitterness" + reagent_state = SOLID + color = "#B060FF" + scannable = 1 + can_overdose_touch = TRUE + +/datum/reagent/tricorlidaze/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.5 + M.adjustOxyLoss(-2 * removed * chem_effective) + M.heal_organ_damage(1 * removed, 1 * removed * chem_effective) + M.adjustToxLoss(-2 * removed * chem_effective) + +/datum/reagent/tricorlidaze/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + M.adjustToxLoss(3 * removed) + affect_blood(M, alien, removed * 0.4) + +/datum/reagent/tricorlidaze/touch_obj(var/obj/O) + if(istype(O, /obj/item/stack/medical/bruise_pack) && round(volume) >= 5) + var/obj/item/stack/medical/bruise_pack/C = O + var/packname = C.name + var/to_produce = min(C.amount, round(volume / 5)) + + var/obj/item/stack/medical/M = C.upgrade_stack(to_produce) + + if(M && M.amount) + holder.my_atom.visible_message("\The [packname] bubbles.") + remove_self(to_produce * 5) + /datum/reagent/cryoxadone name = "Cryoxadone" id = "cryoxadone" @@ -254,6 +392,40 @@ M.heal_organ_damage(30 * removed, 30 * removed * chem_effective) M.adjustToxLoss(-30 * removed * chem_effective) +/datum/reagent/necroxadone + name = "Necroxadone" + id = "necroxadone" + description = "A liquid compound based upon that which is used in the cloning process. Utilized primarily in severe cases of toxic shock." + taste_description = "meat" + reagent_state = LIQUID + color = "#94B21C" + metabolism = REM * 0.5 + mrate_static = TRUE + scannable = 1 + +/datum/reagent/necroxadone/on_mob_life(var/mob/living/carbon/M, var/alien, var/datum/reagents/metabolism/location) + if(M.stat == DEAD && M.has_modifier_of_type(/datum/modifier/bloodpump_corpse)) + affects_dead = TRUE + else + affects_dead = FALSE + + . = ..(M, alien, location) + +/datum/reagent/necroxadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(M.bodytemperature < 170 || (M.stat == DEAD && M.has_modifier_of_type(/datum/modifier/bloodpump_corpse))) + var/chem_effective = 1 + if(alien == IS_SLIME) + if(prob(10)) + to_chat(M, "It's so cold. Something causes your cellular mass to harden sporadically, resulting in seizure-like twitching.") + chem_effective = 0.5 + M.Weaken(20) + M.silent = max(M.silent, 20) + M.make_jittery(4) + if(M.stat != DEAD) + M.adjustCloneLoss(-5 * removed * chem_effective) + M.adjustOxyLoss(-20 * removed * chem_effective) + M.adjustToxLoss(-40 * removed * chem_effective) + /* Painkillers */ /datum/reagent/paracetamol @@ -557,7 +729,7 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M for(var/obj/item/organ/I in H.internal_organs) - if(I.robotic >= ORGAN_ROBOT || !(I.organ_tag in list(O_APPENDIX, O_NUTRIENT, O_PLASMA, O_POLYP))) + if(I.robotic >= ORGAN_ROBOT || !(I.organ_tag in list(O_APPENDIX, O_STOMACH, O_INTESTINE, O_NUTRIENT, O_PLASMA, O_POLYP))) continue if(I.damage > 0) I.damage = max(I.damage - 4 * removed * repair_strength, 0) @@ -621,7 +793,7 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M for(var/obj/item/organ/I in H.internal_organs) - if(I.robotic >= ORGAN_ROBOT || !(I.organ_tag in list(O_HEART, O_RESPONSE, O_ANCHOR, O_EGG))) + if(I.robotic >= ORGAN_ROBOT || !(I.organ_tag in list(O_HEART, O_SPLEEN, O_RESPONSE, O_ANCHOR, O_EGG))) continue if(I.damage > 0) I.damage = max(I.damage - 4 * removed * repair_strength, 0) @@ -798,11 +970,11 @@ if(M.ingested) for(var/datum/reagent/R in M.ingested.reagent_list) if(istype(R, /datum/reagent/ethanol)) - R.remove_self(removed * 5) + R.remove_self(removed * 30) if(M.bloodstr) for(var/datum/reagent/R in M.bloodstr.reagent_list) if(istype(R, /datum/reagent/ethanol)) - R.remove_self(removed * 15) + R.remove_self(removed * 20) /datum/reagent/hyronalin name = "Hyronalin" @@ -865,6 +1037,9 @@ to_chat(M, "Your senses feel unfocused, and divided.") M.add_chemical_effect(CE_ANTIBIOTIC, dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM) +/datum/reagent/spaceacillin/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.8) // Not 100% as effective as injections, though still useful. + /datum/reagent/corophizine name = "Corophizine" id = "corophizine" @@ -932,6 +1107,54 @@ var/obj/item/organ/external/eo = pick(H.organs) //Misleading variable name, 'organs' is only external organs eo.fracture() +/datum/reagent/spacomycaze + name = "Spacomycaze" + id = "spacomycaze" + description = "An all-purpose painkilling antibiotic gel." + taste_description = "oil" + reagent_state = SOLID + color = "#C1C1C8" + metabolism = REM * 0.4 + mrate_static = TRUE + overdose = REAGENTS_OVERDOSE + scannable = 1 + data = 0 + can_overdose_touch = TRUE + +/datum/reagent/spacomycaze/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + M.add_chemical_effect(CE_PAINKILLER, 10) + M.adjustToxLoss(3 * removed) + +/datum/reagent/spacomycaze/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.8) + +/datum/reagent/spacomycaze/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + ..() + if(alien == IS_SLIME) + if(volume <= 0.1 && data != -1) + data = -1 + to_chat(M, "The itching fades...") + else + var/delay = (2 MINUTES) + if(world.time > data + delay) + data = world.time + to_chat(M, "Your skin itches.") + + M.add_chemical_effect(CE_ANTIBIOTIC, dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM) + M.add_chemical_effect(CE_PAINKILLER, 20) // 5 less than paracetamol. + +/datum/reagent/spacomycaze/touch_obj(var/obj/O) + if(istype(O, /obj/item/stack/medical/crude_pack) && round(volume) >= 1) + var/obj/item/stack/medical/crude_pack/C = O + var/packname = C.name + var/to_produce = min(C.amount, round(volume)) + + var/obj/item/stack/medical/M = C.upgrade_stack(to_produce) + + if(M && M.amount) + holder.my_atom.visible_message("\The [packname] bubbles.") + remove_self(to_produce) + /datum/reagent/sterilizine name = "Sterilizine" id = "sterilizine" diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm new file mode 100644 index 0000000000..d9a7483ec1 --- /dev/null +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm @@ -0,0 +1,58 @@ +/* + * Modifier-applying chemicals. + */ + +/datum/reagent/modapplying + name = "brute juice" + id = "berserkmed" + description = "A liquid that is capable of causing a prolonged state of heightened aggression and durability." + taste_description = "metal" + reagent_state = LIQUID + color = "#ff5555" + metabolism = REM + + var/modifier_to_add = /datum/modifier/berserk + var/modifier_duration = 2 SECONDS // How long, per unit dose, will this last? + +/datum/reagent/modapplying/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien == IS_DIONA) + return + M.add_modifier(modifier_to_add, dose * modifier_duration) + +/datum/reagent/modapplying/cryofluid + name = "cryogenic slurry" + id = "cryoslurry" + description = "An incredibly strange liquid that rapidly absorbs thermal energy from materials it contacts." + taste_description = "siberian hellscape" + color = "#4CDBDB" + metabolism = REM * 0.5 + + modifier_to_add = /datum/modifier/cryogelled + modifier_duration = 3 SECONDS + +/datum/reagent/modapplying/cryofluid/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + ..(M, alien, removed) + M.bodytemperature -= removed * 20 + +/datum/reagent/modapplying/cryofluid/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 2.5) + +/datum/reagent/modapplying/cryofluid/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.6) + +/datum/reagent/modapplying/cryofluid/touch_mob(var/mob/M, var/amount) + if(isliving(M)) + var/mob/living/L = M + for(var/I = 1 to rand(1, round(amount + 1))) + L.add_modifier(modifier_to_add, amount * rand(modifier_duration / 2, modifier_duration * 2)) + return + +/datum/reagent/modapplying/cryofluid/touch_turf(var/turf/T, var/amount) + if(istype(T, /turf/simulated/floor/water) && prob(amount)) + T.visible_message("\The [T] crackles loudly as the cryogenic fluid causes it to boil away, leaving behind a hard layer of ice.") + T.ChangeTurf(/turf/simulated/floor/outdoors/ice, 1, 1, TRUE) + else + if(istype(T, /turf/simulated)) + var/turf/simulated/S = T + S.freeze_floor() + return diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm index 954102b732..dea0a6a987 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm @@ -168,6 +168,7 @@ reagent_state = LIQUID color = "#C8A5DC" affects_dead = 1 //This can even heal dead people. + metabolism = 0.1 mrate_static = TRUE //Just in case glass_name = "liquid gold" @@ -180,8 +181,8 @@ M.setCloneLoss(0) M.setOxyLoss(0) M.radiation = 0 - M.heal_organ_damage(5,5) - M.adjustToxLoss(-5) + M.heal_organ_damage(20,20) + M.adjustToxLoss(-20) M.hallucination = 0 M.setBrainLoss(0) M.disabilities = 0 @@ -198,6 +199,28 @@ M.SetConfused(0) M.sleeping = 0 M.jitteriness = 0 + M.radiation = 0 + M.ExtinguishMob() + M.fire_stacks = 0 + if(M.bodytemperature > 310) + M.bodytemperature = max(310, M.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT)) + else if(M.bodytemperature < 311) + M.bodytemperature = min(310, M.bodytemperature + (40 * TEMPERATURE_DAMAGE_COEFFICIENT)) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/wound_heal = 5 + for(var/obj/item/organ/external/O in H.bad_external_organs) + if(O.status & ORGAN_BROKEN) + O.mend_fracture() //Only works if the bone won't rebreak, as usual + for(var/datum/wound/W in O.wounds) + if(W.bleeding()) + W.damage = max(W.damage - wound_heal, 0) + if(W.damage <= 0) + O.wounds -= W + if(W.internal) + W.damage = max(W.damage - wound_heal, 0) + if(W.damage <= 0) + O.wounds -= W /datum/reagent/gold name = "Gold" @@ -506,4 +529,12 @@ M.take_organ_damage(2 * removed, 2 * removed) M.adjustOxyLoss(4 * removed) M.adjustToxLoss(2 * removed) - M.adjustCloneLoss(2 * removed) \ No newline at end of file + M.adjustCloneLoss(2 * removed) + +/datum/reagent/fishbait + name = "Fish Bait" + id = "fishbait" + description = "A natural slurry that particularily appeals to fish." + taste_description = "earthy" + reagent_state = LIQUID + color = "#62764E" \ No newline at end of file diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 59716a5493..971a54bff2 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -11,6 +11,7 @@ metabolism = REM * 0.25 // 0.05 by default. Hopefully enough to get some help, or die horribly, whatever floats your boat filtered_organs = list(O_LIVER, O_KIDNEYS) var/strength = 4 // How much damage it deals per unit + var/skin_danger = 0.2 // The multiplier for how effective the toxin is when making skin contact. /datum/reagent/toxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(strength && alien != IS_DIONA) @@ -23,6 +24,9 @@ M.heal_organ_damage((10/strength) * removed, (10/strength) * removed) //Doses of toxins below 10 units, and 10 strength, are capable of providing useful compounds for repair. M.adjustToxLoss(strength * removed) +/datum/reagent/toxin/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.2) + /datum/reagent/toxin/plasticide name = "Plasticide" id = "plasticide" @@ -50,6 +54,26 @@ color = "#003333" strength = 10 +/datum/reagent/toxin/neurotoxic_protein + name = "toxic protein" + id = "neurotoxic_protein" + description = "A weak neurotoxic chemical commonly found in Sivian fish meat." + taste_description = "fish" + reagent_state = LIQUID + color = "#005555" + strength = 8 + skin_danger = 0.4 + +/datum/reagent/toxin/neurotoxic_protein/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + ..() + if(alien != IS_DIONA) + if(M.canmove && !M.restrained() && istype(M.loc, /turf/space)) + step(M, pick(cardinal)) + if(prob(5)) + M.emote(pick("twitch", "drool", "moan")) + if(prob(20)) + M.adjustBrainLoss(0.1) + //R-UST port // Produced during deuterium synthesis. Super poisonous, SUPER flammable (doesn't need oxygen to burn). /datum/reagent/toxin/hydrophoron @@ -102,6 +126,7 @@ color = "#9D14DB" strength = 30 touch_met = 5 + skin_danger = 1 /datum/reagent/toxin/phoron/touch_mob(var/mob/living/L, var/amount) if(istype(L)) @@ -166,6 +191,7 @@ taste_description = "bitterness" reagent_state = LIQUID strength = 5 + filtered_organs = list(O_SPLEEN) /datum/reagent/toxin/expired_medicine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -207,6 +233,7 @@ color = "#FFFFFF" strength = 0 overdose = REAGENTS_OVERDOSE + filtered_organs = list(O_SPLEEN, O_KIDNEYS) /datum/reagent/toxin/potassium_chloride/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -232,6 +259,7 @@ color = "#FFFFFF" strength = 10 overdose = 20 + filtered_organs = list(O_SPLEEN, O_KIDNEYS) /datum/reagent/toxin/potassium_chlorophoride/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -272,6 +300,35 @@ M.status_flags &= ~FAKEDEATH return ..() +/datum/reagent/toxin/lichpowder + name = "Lich Powder" + id = "lichpowder" + description = "A stablized nerve agent that puts the subject into a strange state of un-death." + reagent_state = SOLID + color = "#666666" + metabolism = REM * 0.75 + strength = 2 + mrate_static = TRUE + +/datum/reagent/toxin/lichpowder/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + ..() + if(alien == IS_DIONA) + return + M.status_flags |= FAKEDEATH + M.adjustOxyLoss(1 * removed) + M.silent = max(M.silent, 10) + M.tod = stationtime2text() + + if(prob(1)) + M.visible_message("[M] wheezes.", "You wheeze sharply... it's cold.") + M.bodytemperature = max(M.bodytemperature - 10 * TEMPERATURE_DAMAGE_COEFFICIENT, T0C - 10) + +/datum/reagent/toxin/lichpowder/Destroy() + if(holder && holder.my_atom && ismob(holder.my_atom)) + var/mob/M = holder.my_atom + M.status_flags &= ~FAKEDEATH + return ..() + /datum/reagent/toxin/fertilizer //Reagents used for plant fertilizers. name = "fertilizer" id = "fertilizer" @@ -327,6 +384,36 @@ if(alien == IS_DIONA) M.adjustToxLoss(50 * removed) +/datum/reagent/toxin/sifslurry + name = "Sivian Sap" + id = "sifsap" + description = "A natural slurry comprised of fluorescent bacteria native to Sif, in the Vir system." + taste_description = "sour" + reagent_state = LIQUID + color = "#C6E2FF" + strength = 2 + overdose = 20 + +/datum/reagent/toxin/sifslurry/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien == IS_DIONA) // Symbiotic bacteria. + M.nutrition += strength * removed + return + else + M.add_modifier(/datum/modifier/slow_pulse, 30 SECONDS) + ..() + +/datum/reagent/toxin/sifslurry/overdose(var/mob/living/carbon/M, var/alien, var/removed) // Overdose effect. + if(alien == IS_DIONA) + return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + overdose_mod *= H.species.chemOD_mod + M.apply_effect(2 * removed,IRRADIATE, 0, 0) + M.apply_effect(5 * removed,DROWSY, 0, 0) + +/datum/reagent/toxin/sifslurry/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.7) + /datum/reagent/acid/polyacid name = "Polytrinic acid" id = "pacid" @@ -337,6 +424,16 @@ power = 10 meltdose = 4 +/datum/reagent/acid/digestive + name = "Digestive acid" + id = "stomacid" + description = "Some form of digestive slurry." + taste_description = "vomit" + reagent_state = LIQUID + color = "#664330" + power = 2 + meltdose = 30 + /datum/reagent/thermite/venom name = "Pyrotoxin" id = "thermite_v" @@ -365,6 +462,7 @@ description = "A biological agent that acts similarly to pepperspray. This compound seems to be particularly cruel, however, capable of permeating the barriers of blood vessels." taste_description = "fire" color = "#B31008" + filtered_organs = list(O_SPLEEN) /datum/reagent/condensedcapsaicin/venom/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) @@ -663,6 +761,7 @@ id = "serotrotium_v" description = "A chemical compound that promotes concentrated production of the serotonin neurotransmitter in humans. This appears to be a biologically produced form, resulting in a specifically toxic nature." taste_description = "chalky bitterness" + filtered_organs = list(O_SPLEEN) /datum/reagent/serotrotium/venom/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) @@ -705,6 +804,7 @@ reagent_state = LIQUID color = "#C8A5DC" overdose = REAGENTS_OVERDOSE + filtered_organs = list(O_SPLEEN) /datum/reagent/impedrezene/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) @@ -910,7 +1010,7 @@ datum/reagent/talum_quem/affect_blood(var/mob/living/carbon/M, var/alien, var/re metabolism = REM * 4 /datum/reagent/irradiated_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - radiation_repository.radiate(get_turf(M), 20) // Irradiate people around you. + SSradiation.radiate(get_turf(M), 20) // Irradiate people around you. M.radiation = max(M.radiation + 5 * removed, 0) // Irradiate you. Because it's inside you. /datum/reagent/neurophage_nanites @@ -921,6 +1021,7 @@ datum/reagent/talum_quem/affect_blood(var/mob/living/carbon/M, var/alien, var/re reagent_state = SOLID color = "#555555" metabolism = REM * 4 + filtered_organs = list(O_SPLEEN) /datum/reagent/neurophage_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) M.adjustBrainLoss(2 * removed) // Their job is to give you a bad time. diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index c3a973402a..b2bb2ec8c3 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -34,6 +34,7 @@ var/reaction_sound = 'sound/effects/bubbles.ogg' var/log_is_important = 0 // If this reaction should be considered important for logging. Important recipes message admins when mixed, non-important ones just log to file. + /datum/chemical_reaction/proc/can_happen(var/datum/reagents/holder) //check that all the required reagents are present if(!holder.has_all_reagents(required_reagents)) @@ -149,18 +150,18 @@ catalysts = list("phoron" = 1) result_amount = 2 -/datum/chemical_reaction/tramadol - name = "Tramadol" - id = "tramadol" - result = "tramadol" - required_reagents = list("inaprovaline" = 1, "ethanol" = 1, "oxygen" = 1) - result_amount = 3 - /datum/chemical_reaction/paracetamol name = "Paracetamol" id = "paracetamol" result = "paracetamol" - required_reagents = list("tramadol" = 1, "sugar" = 1, "water" = 1) + required_reagents = list("inaprovaline" = 1, "nitrogen" = 1, "water" = 1) + result_amount = 2 + +/datum/chemical_reaction/tramadol + name = "Tramadol" + id = "tramadol" + result = "tramadol" + required_reagents = list("paracetamol" = 1, "ethanol" = 1, "oxygen" = 1) result_amount = 3 /datum/chemical_reaction/oxycodone @@ -464,6 +465,13 @@ required_reagents = list("oxygen" = 1, "anti_toxin" = 1, "carbon" = 1) result_amount = 3 +/datum/chemical_reaction/calciumcarbonate + name = "Calcium Carbonate" + id = "calciumcarbonate" + result = "calciumcarbonate" + required_reagents = list("oxygen" = 3, "calcium" = 1, "carbon" = 1) + result_amount = 2 + /datum/chemical_reaction/soporific name = "Soporific" id = "stoxin" @@ -500,6 +508,15 @@ required_reagents = list("carpotoxin" = 5, "stoxin" = 5, "copper" = 5) result_amount = 2 +/datum/chemical_reaction/carpotoxin + name = "Carpotoxin" + id = "carpotoxin" + result = "carpotoxin" + required_reagents = list("spidertoxin" = 2, "biomass" = 1, "sifsap" = 2) + catalysts = list("sifsap" = 10) + inhibitors = list("radium" = 1) + result_amount = 2 + /datum/chemical_reaction/mindbreaker name = "Mindbreaker Toxin" id = "mindbreaker" @@ -737,9 +754,12 @@ var/mob/living/L = holder.my_atom if(L.stat != DEAD) e.amount *= 0.5 + //VOREStation Add Start else holder.clear_reagents() //No more powergaming by creating a tiny amount of this + //VORESTation Add End e.start() + //holder.clear_reagents() //VOREStation Removal return /datum/chemical_reaction/flash_powder @@ -784,8 +804,10 @@ // 100 created volume = 4 heavy range & 7 light range. A few tiles smaller than traitor EMP grandes. // 200 created volume = 8 heavy range & 14 light range. 4 tiles larger than traitor EMP grenades. empulse(location, round(created_volume / 24), round(created_volume / 20), round(created_volume / 18), round(created_volume / 14), 1) + //VOREStation Edit Start if(!isliving(holder.my_atom)) //No more powergaming by creating a tiny amount of this holder.clear_reagents() + //VOREStation Edit End return /datum/chemical_reaction/nitroglycerin @@ -804,10 +826,13 @@ var/mob/living/L = holder.my_atom if(L.stat!=DEAD) e.amount *= 0.5 + //VOREStation Add Start else holder.clear_reagents() //No more powergaming by creating a tiny amount of this + //VOREStation Add End e.start() + //holder.clear_reagents() //VOREStation Removal return /datum/chemical_reaction/napalm @@ -840,8 +865,10 @@ playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) spawn(0) S.start() + //VOREStation Edit Start if(!isliving(holder.my_atom)) //No more powergaming by creating a tiny amount of this holder.clear_reagents() + //VOREStation Edit End return /datum/chemical_reaction/foam @@ -861,8 +888,10 @@ var/datum/effect/effect/system/foam_spread/s = new() s.set_up(created_volume, location, holder, 0) s.start() + //VOREStation Edit Start if(!isliving(holder.my_atom)) //No more powergaming by creating a tiny amount of this holder.clear_reagents() + //VOREStation Edit End return /datum/chemical_reaction/metalfoam @@ -1178,6 +1207,20 @@ new /obj/item/weapon/reagent_containers/food/snacks/chocolatebar(location) return +/datum/chemical_reaction/drinks/coffee + name = "Coffee" + id = "coffee" + result = "coffee" + required_reagents = list("water" = 5, "coffeepowder" = 1) + result_amount = 5 + +/datum/chemical_reaction/drinks/tea + name = "Black tea" + id = "tea" + result = "tea" + required_reagents = list("water" = 5, "teapowder" = 1) + result_amount = 5 + /datum/chemical_reaction/drinks/hot_coco name = "Hot Coco" id = "hot_coco" @@ -1192,6 +1235,34 @@ required_reagents = list("soymilk" = 4, "sacid" = 1) result_amount = 5 +/datum/chemical_reaction/drinks/grapejuice + name = "Grape Juice" + id = "grapejuice" + result = "grapejuice" + required_reagents = list("water" = 3, "instantgrape" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/orangejuice + name = "Orange Juice" + id = "orangejuice" + result = "orangejuice" + required_reagents = list("water" = 3, "instantorange" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/watermelonjuice + name = "Watermelon Juice" + id = "watermelonjuice" + result = "watermelonjuice" + required_reagents = list("water" = 3, "instantwatermelon" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/applejuice + name = "Apple Juice" + id = "applejuice" + result = "applejuice" + required_reagents = list("water" = 3, "instantapple" = 1) + result_amount = 3 + /datum/chemical_reaction/food/ketchup name = "Ketchup" id = "ketchup" @@ -1199,6 +1270,21 @@ required_reagents = list("tomatojuice" = 2, "water" = 1, "sugar" = 1) result_amount = 4 +/datum/chemical_reaction/food/peanutbutter + name = "Peanut Butter" + id = "peanutbutter" + result = "peanutbutter" + required_reagents = list("peanutoil" = 2, "sugar" = 1, "sodiumchloride" = 1) + catalysts = list("enzyme" = 5) + result_amount = 3 + +/datum/chemical_reaction/food/mayonnaise + name = "mayonnaise" + id = "mayo" + result = "mayo" + required_reagents = list("egg" = 9, "cornoil" = 5, "lemonjuice" = 5, "sodiumchloride" = 1) + result_amount = 15 + /datum/chemical_reaction/food/cheesewheel name = "Cheesewheel" id = "cheesewheel" @@ -1827,6 +1913,79 @@ required_reagents = list("lemonjuice" = 1, "sugar" = 1, "water" = 1) result_amount = 3 +/datum/chemical_reaction/drinks/melonade + name = "Melonade" + id = "melonade" + result = "melonade" + required_reagents = list("watermelonjuice" = 1, "sugar" = 1, "sodawater" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/appleade + name = "Appleade" + id = "appleade" + result = "appleade" + required_reagents = list("applejuice" = 1, "sugar" = 1, "sodawater" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/pineappleade + name = "Pineappleade" + id = "pineappleade" + result = "pineappleade" + required_reagents = list("pineapplejuice" = 2, "limejuice" = 1, "sodawater" = 2, "honey" = 1) + result_amount = 5 + +/datum/chemical_reaction/drinks/driverspunch + name = "Driver`s Punch" + id = "driverspunch" + result = "driverspunch" + required_reagents = list("appleade" = 2, "orangejuice" = 1, "mint" = 1, "sodawater" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/mintapplesparkle + name = "Mint Apple Sparkle" + id = "mintapplesparkle" + result = "mintapplesparkle" + required_reagents = list("appleade" = 2, "mint" = 1) + inhibitors = list("sodawater" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/berrycordial + name = "Berry Cordial" + id = "berrycordial" + result = "berrycordial" + required_reagents = list("berryjuice" = 4, "sugar" = 1, "lemonjuice" = 1) + result_amount = 5 + +/datum/chemical_reaction/drinks/tropicalfizz + name = "Tropical Fizz" + id = "tropicalfizz" + result = "tropicalfizz" + required_reagents = list("sodawater" = 6, "berryjuice" = 1, "mint" = 1, "limejuice" = 1, "lemonjuice" = 1, "pineapplejuice" = 1) + inhibitors = list("sugar" = 1) + result_amount = 8 + +/datum/chemical_reaction/drinks/melonspritzer + name = "Melon Spritzer" + id = "melonspritzer" + result = "melonspritzer" + required_reagents = list("watermelonjuice" = 2, "wine" = 2, "applejuice" = 1, "limejuice" = 1) + result_amount = 6 + +/datum/chemical_reaction/drinks/fauxfizz + name = "Faux Fizz" + id = "fauxfizz" + result = "fauxfizz" + required_reagents = list("sodawater" = 2, "berryjuice" = 1, "applejuice" = 1, "limejuice" = 1, "honey" = 1) + inhibitors = list("sugar" = 1) + result_amount = 6 + +/datum/chemical_reaction/drinks/firepunch + name = "Fire Punch" + id = "firepunch" + result = "firepunch" + required_reagents = list("sugar" = 1, "rum" = 2) + result_amount = 3 + /datum/chemical_reaction/drinks/kiraspecial name = "Kira Special" id = "kiraspecial" @@ -1848,6 +2007,13 @@ required_reagents = list("cream" = 1, "ice" = 2, "milk" = 2) result_amount = 5 +/datum/chemical_reaction/drinks/peanutmilkshake + name = "Peanutbutter Milkshake" + id = "peanutmilkshake" + result = "peanutmilkshake" + required_reagents = list("cream" = 1, "ice" = 1, "peanutbutter" = 2, "milk" = 1) + result_amount = 5 + /datum/chemical_reaction/drinks/rewriter name = "Rewriter" id = "rewriter" @@ -2221,6 +2387,13 @@ required_reagents = list("rum" = 3, "limejuice" = 1, "mint" = 1) result_amount = 5 +/datum/chemical_reaction/drinks/virginmojito + name = "Mojito" + id = "virginmojito" + result = "virginmojito" + required_reagents = list("sodawater" = 3, "limejuice" = 1, "mint" = 1, "sugar" = 1) + result_amount = 5 + /datum/chemical_reaction/drinks/piscosour name = "Pisco Sour" id = "piscosour" @@ -2293,6 +2466,27 @@ required_reagents = list("cornoil" = 2, "honey" = 1) result_amount = 3 +/datum/chemical_reaction/drinks/slimeslam + name = "Slick Slime Slammer" + id = "slimeslammer" + result = "slimeslammer" + required_reagents = list("cornoil" = 2, "peanutbutter" = 1) + result_amount = 3 + +/datum/chemical_reaction/drinks/virginsexonthebeach + name = "Virgin Sex On The Beach" + id = "virginsexonthebeach" + result = "virginsexonthebeach" + required_reagents = list("orangejuice" = 3, "grenadine" = 2) + result_amount = 4 + +/datum/chemical_reaction/drinks/sexonthebeach + name = "Sex On The Beach" + id = "sexonthebeach" + result = "sexonthebeach" + required_reagents = list("orangejuice" = 3, "grenadine" = 2, "vodka" = 1) + result_amount = 6 + /datum/chemical_reaction/drinks/eggnog name = "Eggnog" id = "eggnog" @@ -2378,4 +2572,29 @@ id = "biomass" result = "biomass" required_reagents = list("protein" = 1, "sugar" = 1, "phoron" = 1) - result_amount = 6 // Roughly 120u per phoron sheet //VOREStation Edit + result_amount = 1 // Roughly 20u per phoron sheet + +// Neutralization. + +/datum/chemical_reaction/neutralize_neurotoxic_protein + name = "Neutralize Toxic Proteins" + id = "neurotoxic_protein_neutral" + result = "protein" + required_reagents = list("anti_toxin" = 1, "neurotoxic_protein" = 2) + result_amount = 2 + +/datum/chemical_reaction/neutralize_carpotoxin + name = "Neutralize Carpotoxin" + id = "carpotoxin_neutral" + result = "protein" + required_reagents = list("radium" = 1, "carpotoxin" = 1, "sifsap" = 1) + catalysts = list("sifsap" = 10) + result_amount = 2 + +/datum/chemical_reaction/neutralize_spidertoxin + name = "Neutralize Spidertoxin" + id = "spidertoxin_neutral" + result = "protein" + required_reagents = list("radium" = 1, "spidertoxin" = 1, "sifsap" = 1) + catalysts = list("sifsap" = 10) + result_amount = 2 diff --git a/code/modules/reagents/Chemistry-Recipes_vr.dm b/code/modules/reagents/Chemistry-Recipes_vr.dm index 098cae99f0..c6d948ac35 100644 --- a/code/modules/reagents/Chemistry-Recipes_vr.dm +++ b/code/modules/reagents/Chemistry-Recipes_vr.dm @@ -59,6 +59,7 @@ if(H.stat == DEAD && (/mob/living/carbon/human/proc/reconstitute_form in H.verbs)) //no magical regen for non-regenners, and can't force the reaction on live ones if(H.hasnutriment()) // make sure it actually has the conditions to revive if(H.revive_ready >= 1) // if it's not reviving, start doing so + H.revive_ready = REVIVING_READY // overrides the normal cooldown H.visible_message("[H] shudders briefly, then relaxes, faint movements stirring within.") H.chimera_regenerate() else if (/mob/living/carbon/human/proc/hatch in H.verbs)// already reviving, check if they're ready to hatch @@ -101,7 +102,7 @@ name = "Vermicetol" id = "vermicetol" result = "vermicetol" - required_reagents = list("kelotane" = 1, "dermaline" = 1, "shockchem" = 1, "phoron" = 0.1) + required_reagents = list("bicaridine" = 2, "shockchem" = 1, "phoron" = 0.1) catalysts = list("phoron" = 5) result_amount = 3 @@ -340,4 +341,10 @@ if(prob(50)) for(var/j = 1, j <= rand(1, 3), j++) step(C, pick(NORTH,SOUTH,EAST,WEST)) -*/ \ No newline at end of file +*/ + +/datum/chemical_reaction/food/syntiflesh + required_reagents = list("blood" = 5, "clonexadone" = 1) + +/datum/chemical_reaction/biomass + result_amount = 6 // Roughly 120u per phoron sheet diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index 70142fabd8..ddcd173cb7 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -8,7 +8,7 @@ volume = CARTRIDGE_VOLUME_LARGE amount_per_transfer_from_this = 50 // Large, but inaccurate. Use a chem dispenser or beaker for accuracy. - possible_transfer_amounts = list(50, 100) + possible_transfer_amounts = list(50, 100, 250, 500) unacidable = 1 var/spawn_reagent = null diff --git a/code/modules/reagents/dispenser/cartridge_presets.dm b/code/modules/reagents/dispenser/cartridge_presets.dm index a29d7ac9f8..9f7ddc071e 100644 --- a/code/modules/reagents/dispenser/cartridge_presets.dm +++ b/code/modules/reagents/dispenser/cartridge_presets.dm @@ -30,6 +30,7 @@ ethanol spawn_reagent = "ethanol" sacid spawn_reagent = "sacid" tungsten spawn_reagent = "tungsten" + calcium spawn_reagent = "calcium" // Bar, alcoholic beer spawn_reagent = "beer" @@ -45,6 +46,7 @@ ale spawn_reagent = "ale" mead spawn_reagent = "mead" bitters spawn_reagent = "bitters" + cider spawn_reagent = "cider" // Bar, soft ice spawn_reagent = "ice" diff --git a/code/modules/reagents/dispenser/dispenser2_energy.dm b/code/modules/reagents/dispenser/dispenser2_energy.dm index c889b2fe9d..ee5f0b9484 100644 --- a/code/modules/reagents/dispenser/dispenser2_energy.dm +++ b/code/modules/reagents/dispenser/dispenser2_energy.dm @@ -52,11 +52,11 @@ /obj/machinery/chemical_dispenser/bar_alc dispense_reagents = list( "lemon_lime", "sugar", "orangejuice", "limejuice", "sodawater", "tonic", "beer", "kahlua", - "whiskey", "wine", "vodka", "gin", "rum", "tequilla", "vermouth", "cognac", "ale", "mead", "bitters" + "whiskey", "wine", "vodka", "cider", "gin", "rum", "tequilla", "vermouth", "cognac", "ale", "mead", "bitters" ) /obj/machinery/chemical_dispenser/bar_coffee dispense_reagents = list( "coffee", "cafe_latte", "soy_latte", "hot_coco", "milk", "cream", "tea", "ice", "orangejuice", "lemonjuice", "limejuice", "berryjuice", "mint" - ) \ No newline at end of file + ) diff --git a/code/modules/reagents/dispenser/dispenser_presets.dm b/code/modules/reagents/dispenser/dispenser_presets.dm index 05477d9cdd..d3aa3c0133 100644 --- a/code/modules/reagents/dispenser/dispenser_presets.dm +++ b/code/modules/reagents/dispenser/dispenser_presets.dm @@ -21,7 +21,8 @@ /obj/item/weapon/reagent_containers/chem_disp_cartridge/ethanol, /obj/item/weapon/reagent_containers/chem_disp_cartridge/sugar, /obj/item/weapon/reagent_containers/chem_disp_cartridge/sacid, - /obj/item/weapon/reagent_containers/chem_disp_cartridge/tungsten + /obj/item/weapon/reagent_containers/chem_disp_cartridge/tungsten, + /obj/item/weapon/reagent_containers/chem_disp_cartridge/calcium ) /obj/machinery/chemical_dispenser/ert @@ -111,6 +112,7 @@ /obj/item/weapon/reagent_containers/chem_disp_cartridge/tequila, /obj/item/weapon/reagent_containers/chem_disp_cartridge/vermouth, /obj/item/weapon/reagent_containers/chem_disp_cartridge/cognac, + /obj/item/weapon/reagent_containers/chem_disp_cartridge/cider, /obj/item/weapon/reagent_containers/chem_disp_cartridge/ale, /obj/item/weapon/reagent_containers/chem_disp_cartridge/mead ) @@ -139,4 +141,4 @@ /obj/item/weapon/reagent_containers/chem_disp_cartridge/berry, - ) \ No newline at end of file + ) diff --git a/code/modules/reagents/dispenser/supply.dm b/code/modules/reagents/dispenser/supply.dm index e6488d9d7e..228c5dfeef 100644 --- a/code/modules/reagents/dispenser/supply.dm +++ b/code/modules/reagents/dispenser/supply.dm @@ -192,6 +192,7 @@ SEC_PACK(radium, /obj/item/weapon/reagent_containers/chem_disp_cartridge/radi SEC_PACK(ethanol, /obj/item/weapon/reagent_containers/chem_disp_cartridge/ethanol, "Reagent refill - Ethanol", "ethanol reagent cartridge crate", 15, access_chemistry) SEC_PACK(sacid, /obj/item/weapon/reagent_containers/chem_disp_cartridge/sacid, "Reagent refill - Sulfuric Acid", "sulfuric acid reagent cartridge crate", 15, access_chemistry) SEC_PACK(tungsten, /obj/item/weapon/reagent_containers/chem_disp_cartridge/tungsten, "Reagent refill - Tungsten", "tungsten reagent cartridge crate", 15, access_chemistry) +SEC_PACK(calcium, /obj/item/weapon/reagent_containers/chem_disp_cartridge/calcium, "Reagent refill - Calcium", "calcium reagent cartridge crate", 15, access_chemistry) // Bar-restricted (alcoholic drinks) // Datum path Contents type Supply pack name Container name Cost Container access diff --git a/code/modules/reagents/distilling/Distilling-Recipes.dm b/code/modules/reagents/distilling/Distilling-Recipes.dm new file mode 100644 index 0000000000..d610e32e9b --- /dev/null +++ b/code/modules/reagents/distilling/Distilling-Recipes.dm @@ -0,0 +1,216 @@ +/datum/chemical_reaction/distilling +// name = null +// id = null +// result = null +// required_reagents = list() +// catalysts = list() +// inhibitors = list() +// result_amount = 0 + + //how far the reaction proceeds each time it is processed. Used with either REACTION_RATE or HALF_LIFE macros. + reaction_rate = HALF_LIFE(6) + + //if less than 1, the reaction will be inhibited if the ratio of products/reagents is too high. + //0.5 = 50% yield -> reaction will only proceed halfway until products are removed. +// yield = 1.0 + + //If limits on reaction rate would leave less than this amount of any reagent (adjusted by the reaction ratios), + //the reaction goes to completion. This is to prevent reactions from going on forever with tiny reagent amounts. +// min_reaction = 2 + + mix_message = "The solution churns." + reaction_sound = 'sound/effects/slosh.ogg' + +// log_is_important = 0 // If this reaction should be considered important for logging. Important recipes message admins when mixed, non-important ones just log to file. + + var/list/temp_range = list(T0C, T20C) + var/temp_shift = 0 // How much the temperature changes when the reaction occurs. + +/datum/chemical_reaction/distilling/can_happen(var/datum/reagents/holder) + //check that all the required reagents are present + if(!holder.has_all_reagents(required_reagents)) + return 0 + + //check that all the required catalysts are present in the required amount + if(!holder.has_all_reagents(catalysts)) + return 0 + + //check that none of the inhibitors are present in the required amount + if(holder.has_any_reagent(inhibitors)) + return 0 + + if(!istype(holder.my_atom, /obj/item/weapon/reagent_containers/glass/distilling)) + return 0 + + else // Super special temperature check. + var/obj/item/weapon/reagent_containers/glass/distilling/D = holder.my_atom + var/obj/machinery/portable_atmospherics/powered/reagent_distillery/RD = D.Master + if(RD.current_temp < temp_range[1] || RD.current_temp > temp_range[2]) + return 0 + + return 1 + +/datum/chemical_reaction/distilling/on_reaction(var/datum/reagents/holder, var/created_volume) + if(istype(holder.my_atom, /obj/item/weapon/reagent_containers/glass/distilling)) + var/obj/item/weapon/reagent_containers/glass/distilling/D = holder.my_atom + var/obj/machinery/portable_atmospherics/powered/reagent_distillery/RD = D.Master + RD.current_temp += temp_shift + return + +// Subtypes // + +// Biomass +/datum/chemical_reaction/distilling/biomass + name = "Distilling Biomass" + id = "distill_biomass" + result = "biomass" + required_reagents = list("blood" = 1, "sugar" = 1, "phoron" = 0.5) + result_amount = 1 // 40 units per sheet, requires actually using the machine, and having blood to spare. + + temp_range = list(T20C + 80, T20C + 130) + temp_shift = -2 + +// Medicinal +/datum/chemical_reaction/distilling/inaprovalaze + name = "Distilling Inaprovalaze" + id = "distill_inaprovalaze" + result = "inaprovalaze" + required_reagents = list("inaprovaline" = 2, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 100, T0C + 120) + +/datum/chemical_reaction/distilling/bicaridaze + name = "Distilling Bicaridaze" + id = "distill_bicaridaze" + result = "bicaridaze" + required_reagents = list("bicaridine" = 2, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 110, T0C + 130) + +/datum/chemical_reaction/distilling/dermalaze + name = "Distilling Dermalaze" + id = "distill_dermalaze" + result = "dermalaze" + required_reagents = list("dermaline" = 2, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 115, T0C + 130) + +/datum/chemical_reaction/distilling/spacomycaze + name = "Distilling Spacomycaze" + id = "distill_spacomycaze" + result = "spacomycaze" + required_reagents = list("paracetamol" = 1, "spaceacillin" = 1, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 100, T0C + 120) + +/datum/chemical_reaction/distilling/tricorlidaze + name = "Distilling Tricorlidaze" + id = "distill_tricorlidaze" + result = "tricorlidaze" + required_reagents = list("tricordrazine" = 1, "sterilizine" = 1, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 100, T0C + 120) + +// Alcohol +/datum/chemical_reaction/distilling/beer + name = "Distilling Beer" + id = "distill_beer" + result = "beer" + required_reagents = list("nutriment" = 1, "water" = 1, "sugar" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(30) + + temp_range = list(T20C, T20C + 2) + +/datum/chemical_reaction/distilling/ale + name = "Distilling Ale" + id = "distill_ale" + result = "ale" + required_reagents = list("nutriment" = 1, "beer" = 1) + inhibitors = list("water" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(30) + + temp_shift = 0.5 + temp_range = list(T0C + 7, T0C + 13) + +// Unique +/datum/chemical_reaction/distilling/berserkjuice + name = "Distilling Brute Juice" + id = "distill_brutejuice" + result = "berserkmed" + required_reagents = list("biomass" = 1, "hyperzine" = 3, "synaptizine" = 2, "phoron" = 1) + result_amount = 3 + + temp_range = list(T0C + 600, T0C + 700) + temp_shift = 4 + +/datum/chemical_reaction/distilling/berserkjuice/on_reaction(var/datum/reagents/holder, var/created_volume) + ..() + + if(prob(1)) + var/turf/T = get_turf(holder.my_atom) + explosion(T, -1, rand(-1, 1), rand(1,2), rand(3,5)) + return + +/datum/chemical_reaction/distilling/cryogel + name = "Distilling Cryogellatin" + id = "distill_cryoslurry" + result = "cryoslurry" + required_reagents = list("frostoil" = 7, "enzyme" = 3, "plasticide" = 3, "foaming_agent" = 2) + inhibitors = list("water" = 5) + result_amount = 1 + + temp_range = list(0, 15) + temp_shift = 20 + +/datum/chemical_reaction/distilling/cryogel/on_reaction(var/datum/reagents/holder, var/created_volume) + ..() + + if(prob(1)) + var/turf/T = get_turf(holder.my_atom) + var/datum/effect/effect/system/smoke_spread/frost/F = new (holder.my_atom) + F.set_up(6, 0, T) + F.start() + return + +/datum/chemical_reaction/distilling/lichpowder + name = "Distilling Lichpowder" + id = "distill_lichpowder" + result = "lichpowder" + required_reagents = list("zombiepowder" = 2, "leporazine" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(8) + + temp_range = list(T0C + 100, T0C + 150) + +/datum/chemical_reaction/distilling/necroxadone + name = "Distilling Necroxadone" + id = "distill_necroxadone" + result = "necroxadone" + required_reagents = list("lichpowder" = 1, "cryoxadone" = 1, "carthatoline" = 1) + result_amount = 2 + + catalysts = list("phoron" = 5) + + reaction_rate = HALF_LIFE(20) + + temp_range = list(T0C + 90, T0C + 95) diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm new file mode 100644 index 0000000000..b86bcc7435 --- /dev/null +++ b/code/modules/reagents/distilling/distilling.dm @@ -0,0 +1,317 @@ + +/* + * Distillery, used for over-time temperature-based mixes. + */ + +/obj/machinery/portable_atmospherics/powered/reagent_distillery + name = "chemical distillery" + desc = "A complex machine utilizing state-of-the-art components to mix chemicals at different temperatures." + use_power = 1 + + icon = 'icons/obj/machines/reagent.dmi' + icon_state = "distiller" + var/base_state // The string var used in update icon for overlays, either set manually or initialized. + + power_rating = 3000 + power_losses = 240 + + var/on = FALSE + + var/target_temp = T20C + + var/max_temp = T20C + 300 + var/min_temp = T0C - 10 + + var/current_temp = T20C + + var/use_atmos = FALSE // If true, this machine will be connectable to ports, and use gas mixtures as the source of heat, rather than its internal controls. + + var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine") + var/static/radial_use = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_use") + + var/static/radial_pump = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_pump") + + var/static/radial_eject_input = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject_input") + var/static/radial_eject_output = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject_output") + + var/static/radial_adjust_temp = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_temp") + + var/static/radial_install_input = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_add") + var/static/radial_install_output = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_add") + + var/static/radial_inspectgauges = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_lookat") + + var/static/radial_mix = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_mix") + +// Overlay holders so we don't have to constantly remake them. + var/image/overlay_output_beaker + var/image/overlay_input_beaker + var/image/overlay_off + var/image/overlay_ready + var/image/overlay_cooling + var/image/overlay_heating + var/image/overlay_dumping + var/image/overlay_connected + +// Our unique beaker, used in its unique recipes to ensure things can only react inside this machine and minimize oddities from trying to transfer to a machine and back. + var/obj/item/weapon/reagent_containers/glass/distilling/Reservoir + + var/obj/item/weapon/reagent_containers/glass/InputBeaker + var/obj/item/weapon/reagent_containers/glass/OutputBeaker + +// A multiplier for the production amount. This should really only ever be lower than one, otherwise you end up with duping. + var/efficiency = 1 + +/obj/item/weapon/reagent_containers/glass/distilling + name = "distilling chamber" + desc = "You should not be seeing this." + volume = 600 + + var/obj/machinery/portable_atmospherics/powered/reagent_distillery/Master + +/obj/item/weapon/reagent_containers/glass/distilling/Destroy() + Master = null + ..() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/Initialize() + ..() + + Reservoir = new (src) + Reservoir.Master = src + + if(!base_state) + base_state = icon_state + + setup_overlay_vars() + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/proc/setup_overlay_vars() + overlay_output_beaker = image(icon = src.icon, icon_state = "[base_state]-output") + overlay_input_beaker = image(icon = src.icon, icon_state = "[base_state]-input") + overlay_off = image(icon = src.icon, icon_state = "[base_state]-bad") + overlay_ready = image(icon = src.icon, icon_state = "[base_state]-good") + overlay_cooling = image(icon = src.icon, icon_state = "[base_state]-cool") + overlay_heating = image(icon = src.icon, icon_state = "[base_state]-heat") + overlay_dumping = image(icon = src.icon, icon_state = "[base_state]-dump") + overlay_connected = image(icon = src.icon, icon_state = "[base_state]-connector") + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/Destroy() + qdel(Reservoir) + Reservoir = null + if(InputBeaker) + qdel(InputBeaker) + InputBeaker = null + if(OutputBeaker) + qdel(OutputBeaker) + OutputBeaker = null + + ..() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user) + var/list/options = list() + options["examine"] = radial_examine + options["use"] = radial_use + options["inspect gauges"] = radial_inspectgauges + options["pulse agitator"] = radial_mix + + if(InputBeaker) + options["eject input"] = radial_eject_input + if(OutputBeaker) + options["eject output"] = radial_eject_output + + if(!use_atmos) + options["adjust temp"] = radial_adjust_temp + + if(length(options) < 1) + return + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(user, src, options, require_near = !issilicon(user)) + + switch(choice) + if("examine") + examine(user) + + if("use") + if(powered()) + on = !on + to_chat(user, "You turn \the [src] [on ? "on" : "off"].") + + if("inspect gauges") + to_chat(user, "\The [src]'s gauges read:") + if(!use_atmos) + to_chat(user, "- Target Temperature: [target_temp]") + to_chat(user, "- Temperature: [current_temp]") + + if("pulse agitator") + to_chat(user, "You press \the [src]'s chamber agitator button.") + if(on) + visible_message("\The [src] rattles to life.") + Reservoir.reagents.handle_reactions() + else + spawn(1 SECOND) + to_chat(user, "Nothing happens..") + + if("eject input") + if(InputBeaker) + InputBeaker.forceMove(get_turf(src)) + InputBeaker = null + + if("eject output") + if(OutputBeaker) + OutputBeaker.forceMove(get_turf(src)) + OutputBeaker = null + + if("adjust temp") + target_temp = input("Choose a target temperature.", "Temperature.", T20C) as num + target_temp = CLAMP(target_temp, min_temp, max_temp) + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/list/options = list() + if(istype(W, /obj/item/weapon/reagent_containers/glass)) + if(!InputBeaker) + options["install input"] = radial_install_input + if(!OutputBeaker) + options["install output"] = radial_install_output + + if(!options || !options.len) + update_icon() + return ..() + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(user, src, options, require_near = TRUE) // No telekinetics. + + switch(choice) + if("install input") + if(!InputBeaker) + user.drop_from_inventory(W) + W.add_fingerprint(user) + W.forceMove(src) + InputBeaker = W + + if("install output") + if(!OutputBeaker) + user.drop_from_inventory(W) + W.add_fingerprint(user) + W.forceMove(src) + OutputBeaker = W + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/use_power(var/amount, var/chan = -1) + last_power_draw = amount + if(use_cell && cell && cell.charge) + var/cellcharge = cell.charge + cell.use(amount) + + var/celldifference = max(0, cellcharge - cell.charge) + + amount = celldifference + + var/area/A = get_area(src) + if(!A || !isarea(A)) + return + if(chan == -1) + chan = power_channel + A.use_power(amount, chan) + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/process() + ..() + + var/run_pump = FALSE + + if(InputBeaker || OutputBeaker) + run_pump = TRUE + + var/avg_temp = 0 + var/avg_pressure = 0 + + if(connected_port && connected_port.network.line_members.len) + var/list/members = list() + var/datum/pipe_network/Net = connected_port.network + members = Net.line_members.Copy() + + for(var/datum/pipeline/Line in members) + avg_pressure += Line.air.return_pressure() + avg_temp += Line.air.temperature + + avg_temp /= members.len + avg_pressure /= members.len + + if(!powered()) + on = FALSE + + if(!on || (use_atmos && (!connected_port || avg_pressure < 1000))) + current_temp = round((current_temp + T20C) / 2) + + else if(on) + if(!use_atmos) + if(current_temp != round(target_temp)) + var/shift_mod = 0 + if(current_temp < target_temp) + shift_mod = 1 + else if(current_temp > target_temp) + shift_mod = -1 + current_temp = CLAMP(round((current_temp + 1 * shift_mod) + (rand(-5, 5) / 10)), min_temp, max_temp) + use_power(power_rating * CELLRATE) + else if(connected_port && avg_pressure > 1000) + current_temp = round((current_temp + avg_temp) / 2) + else if(!run_pump) + visible_message("\The [src]'s motors wind down.") + on = FALSE + + if(InputBeaker && Reservoir.reagents.total_volume < Reservoir.reagents.maximum_volume) + InputBeaker.reagents.trans_to_holder(Reservoir.reagents, amount = rand(10,20)) + + if(OutputBeaker && OutputBeaker.reagents.total_volume < OutputBeaker.reagents.maximum_volume) + use_power(power_rating * CELLRATE * 0.5) + Reservoir.reagents.trans_to_holder(OutputBeaker.reagents, amount = rand(1, 5)) + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/update_icon() + ..() + cut_overlays() + + if(InputBeaker) + add_overlay(overlay_input_beaker) + + if(OutputBeaker) + add_overlay(overlay_output_beaker) + + if(on) + if(OutputBeaker && OutputBeaker.reagents.total_volume < OutputBeaker.reagents.maximum_volume) + add_overlay(overlay_dumping) + else if(current_temp == round(target_temp)) + add_overlay(overlay_ready) + else if(current_temp < target_temp) + add_overlay(overlay_heating) + else + add_overlay(overlay_cooling) + + else + add_overlay(overlay_off) + + if(connected_port) + add_overlay(overlay_connected) + +/* + * Subtypes + */ + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial + name = "industrial chemical distillery" + desc = "A gas-operated variant of a chemical distillery. Able to reach much higher, and lower, temperatures through the use of treated gas." + + use_atmos = TRUE diff --git a/code/modules/reagents/reagent_containers/blood_pack_vr.dm b/code/modules/reagents/reagent_containers/blood_pack_vr.dm index e1d616aeca..b4e27fb95a 100644 --- a/code/modules/reagents/reagent_containers/blood_pack_vr.dm +++ b/code/modules/reagents/reagent_containers/blood_pack_vr.dm @@ -19,3 +19,40 @@ return else return + +/obj/item/weapon/reagent_containers/blood/prelabeled + name = "IV Pack" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted." + +/obj/item/weapon/reagent_containers/blood/prelabeled/update_iv_label() + return + +/obj/item/weapon/reagent_containers/blood/prelabeled/APlus + name = "IV Pack (A+)" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted. This one is labeled A+" + blood_type = "A+" + +/obj/item/weapon/reagent_containers/blood/prelabeled/AMinus + name = "IV Pack (A-)" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted. This one is labeled A_" + blood_type = "A-" + +/obj/item/weapon/reagent_containers/blood/prelabeled/BPlus + name = "IV Pack (B+)" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted. This one is labeled B+" + blood_type = "B+" + +/obj/item/weapon/reagent_containers/blood/prelabeled/BMinus + name = "IV Pack (B-)" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted. This one is labeled B-" + blood_type = "B-" + +/obj/item/weapon/reagent_containers/blood/prelabeled/OPlus + name = "IV Pack (O+)" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted. This one is labeled O+" + blood_type = "O+" + +/obj/item/weapon/reagent_containers/blood/prelabeled/OMinus + name = "IV Pack (O-)" + desc = "Holds liquids used for transfusion. This one's label seems to be hardprinted. This one is labeled O-" + blood_type = "O-" \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index df99bca9e1..40c6c52923 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -132,7 +132,7 @@ recharge_time = 3 volume = 60 possible_transfer_amounts = list(5, 10, 20, 30) - reagent_ids = list("ale", "beer", "berryjuice", "bitters", "coffee", "cognac", "cola", "dr_gibb", "egg", "gin", "gingerale", "hot_coco", "ice", "icetea", "kahlua", "lemonjuice", "lemon_lime", "limejuice", "mead", "milk", "mint", "orangejuice", "rum", "sake", "sodawater", "soymilk", "space_up", "spacemountainwind", "specialwhiskey", "sugar", "tea", "tequilla", "tomatojuice", "tonic", "vermouth", "vodka", "water", "watermelonjuice", "whiskey", "wine") + reagent_ids = list("ale", "cider", "beer", "berryjuice", "bitters", "coffee", "cognac", "cola", "dr_gibb", "egg", "gin", "gingerale", "hot_coco", "ice", "icetea", "kahlua", "lemonjuice", "lemon_lime", "limejuice", "mead", "milk", "mint", "orangejuice", "rum", "sake", "sodawater", "soymilk", "space_up", "spacemountainwind", "specialwhiskey", "sugar", "tea", "tequilla", "tomatojuice", "tonic", "vermouth", "vodka", "water", "watermelonjuice", "whiskey", "wine") /obj/item/weapon/reagent_containers/borghypo/service/attack(var/mob/M, var/mob/user) return diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 4cc781fdea..a22257a8e2 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -14,7 +14,7 @@ possible_transfer_amounts = list(5,10,15,25,30,60) volume = 60 w_class = ITEMSIZE_SMALL - flags = OPENCONTAINER + flags = OPENCONTAINER | NOCONDUCT unacidable = 1 //glass doesn't dissolve in acid var/label_text = "" @@ -44,7 +44,8 @@ /obj/machinery/smartfridge/, /obj/machinery/biogenerator, /obj/structure/frame, - /obj/machinery/radiocarbon_spectrometer + /obj/machinery/radiocarbon_spectrometer, + /obj/machinery/portable_atmospherics/powered/reagent_distillery ) /obj/item/weapon/reagent_containers/glass/Initialize() @@ -131,12 +132,15 @@ update_name_label() if(istype(W,/obj/item/weapon/storage/bag)) ..() + if(W && W.w_class <= w_class && (flags & OPENCONTAINER)) + to_chat(user, "You dip \the [W] into \the [src].") + reagents.touch_obj(W, reagents.total_volume) /obj/item/weapon/reagent_containers/glass/proc/update_name_label() if(label_text == "") name = base_name - else if(length(label_text) > 10) - var/short_label_text = copytext(label_text, 1, 11) + else if(length(label_text) > 20) + var/short_label_text = copytext(label_text, 1, 21) name = "[base_name] ([short_label_text]...)" else name = "[base_name] ([label_text])" diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 4b5949595b..6b7910fab3 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -44,7 +44,14 @@ to_chat(user, "You cannot inject a robotic limb.") return - if(!H.stat) + //VOREStation Add Start - Adds Prototype Hypo functionality + if(H != user && prototype) + to_chat(user, "You begin injecting [H] with \the [src].") + to_chat(H, " [user] is trying to inject you with \the [src]!") + if(!do_after(user, 30, H)) + return + //VOREstation Add End + else if(!H.stat && !prototype) //VOREStation Edit if(H != user) if(H.a_intent != I_HELP) to_chat(user, "[H] is resisting your attempt to inject them with \the [src].") diff --git a/code/modules/reagents/reagent_containers/hypospray_vr.dm b/code/modules/reagents/reagent_containers/hypospray_vr.dm index 9f52d9f699..526ae77fba 100644 --- a/code/modules/reagents/reagent_containers/hypospray_vr.dm +++ b/code/modules/reagents/reagent_containers/hypospray_vr.dm @@ -6,7 +6,7 @@ amount_per_transfer_from_this = 10 volume = 10 -/obj/item/weapon/reagent_containers/hypospray/autoinjector/miner/New() +/obj/item/weapon/reagent_containers/hypospray/autoinjector/miner/Initialize() ..() reagents.add_reagent("bicaridine", 5) reagents.add_reagent("tricordrazine", 3) @@ -18,7 +18,15 @@ desc = "Contains emergency trauma autoinjectors." icon_state = "syringe" -/obj/item/weapon/storage/box/traumainjectors/New() +/obj/item/weapon/storage/box/traumainjectors/Initialize() ..() for (var/i = 1 to 7) new /obj/item/weapon/reagent_containers/hypospray/autoinjector/miner(src) + +/obj/item/weapon/reagent_containers/hypospray + var/prototype = 0 + +/obj/item/weapon/reagent_containers/hypospray/science + name = "prototype hypospray" + desc = "This reproduction hypospray is nearly a perfect replica of the early model DeForest hyposprays, sharing many of the same features. However, there are additional safety measures installed to prevent unwanted injections." + prototype = 1 diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm new file mode 100644 index 0000000000..007669286e --- /dev/null +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -0,0 +1,82 @@ + +/* + * Patches. A subtype of pills, in order to inherit the possible future produceability within chem-masters, and dissolving. + */ + +/obj/item/weapon/reagent_containers/pill/patch + name = "patch" + desc = "A patch." + icon = 'icons/obj/chemical.dmi' + icon_state = null + item_state = "pill" + + base_state = "patch" + + possible_transfer_amounts = null + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + volume = 60 + + var/pierce_material = FALSE // If true, the patch can be used through thick material. + +/obj/item/weapon/reagent_containers/pill/patch/attack(mob/M as mob, mob/user as mob) + var/mob/living/L = user + + if(M == L) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(check_zone(L.zone_sel.selecting)) + if(!affecting) + to_chat(user, "The limb is missing!") + return + if(affecting.status >= ORGAN_ROBOT) + to_chat(user, "\The [src] won't work on a robotic limb!") + return + + if(!H.can_inject(user, FALSE, L.zone_sel.selecting, pierce_material)) + to_chat(user, "\The [src] can't be applied through such a thick material!") + return + + to_chat(H, "\The [src] is placed on your [affecting].") + M.drop_from_inventory(src) //icon update + if(reagents.total_volume) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_TOUCH) + qdel(src) + return 1 + + else if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(check_zone(L.zone_sel.selecting)) + if(!affecting) + to_chat(user, "The limb is missing!") + return + if(affecting.status >= ORGAN_ROBOT) + to_chat(user, "\The [src] won't work on a robotic limb!") + return + + if(!H.can_inject(user, FALSE, L.zone_sel.selecting, pierce_material)) + to_chat(user, "\The [src] can't be applied through such a thick material!") + return + + user.visible_message("[user] attempts to place \the [src] onto [H]`s [affecting].") + + user.setClickCooldown(user.get_attack_speed(src)) + if(!do_mob(user, M)) + return + + user.drop_from_inventory(src) //icon update + user.visible_message("[user] applies \the [src] to [H].") + + var/contained = reagentlist() + add_attack_logs(user,M,"Applied a patch containing [contained]") + + to_chat(H, "\The [src] is placed on your [affecting].") + M.drop_from_inventory(src) //icon update + + if(reagents.total_volume) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_TOUCH) + qdel(src) + + return 1 + + return 0 \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index b2f5d39cde..5ca47a6d3b 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -7,6 +7,9 @@ icon = 'icons/obj/chemical.dmi' icon_state = null item_state = "pill" + + var/base_state = "pill" + possible_transfer_amounts = null w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS @@ -15,7 +18,7 @@ /obj/item/weapon/reagent_containers/pill/Initialize() . = ..() if(!icon_state) - icon_state = "pill[rand(1, 20)]" + icon_state = "[base_state][rand(1, 4)]" //preset pills only use colour changing or unique icons /obj/item/weapon/reagent_containers/pill/attack(mob/M as mob, mob/user as mob) if(M == user) @@ -92,27 +95,29 @@ //Pills /obj/item/weapon/reagent_containers/pill/antitox - name = "Anti-toxins pill" - desc = "Neutralizes many common toxins. Contains 25 units of Dylovene." - icon_state = "pill17" + name = "Dylovene (25u)" + desc = "Neutralizes many common toxins." + icon_state = "pill1" /obj/item/weapon/reagent_containers/pill/antitox/Initialize() . = ..() reagents.add_reagent("anti_toxin", 25) + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/tox name = "Toxins pill" - desc = "Highly toxic." //this is cooler without "contains 50u toxin" - icon_state = "pill5" + desc = "Highly toxic." + icon_state = "pill4" /obj/item/weapon/reagent_containers/pill/tox/Initialize() . = ..() reagents.add_reagent("toxin", 50) + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/cyanide - name = "Cyanide pill" - desc = "Don't swallow this." //this is cooler without "contains 50u cyanide" - icon_state = "pill5" + name = "Strange pill" + desc = "It's marked 'KCN'. Smells vaguely of almonds." + icon_state = "pill9" /obj/item/weapon/reagent_containers/pill/cyanide/Initialize() . = ..() @@ -121,186 +126,195 @@ /obj/item/weapon/reagent_containers/pill/adminordrazine name = "Adminordrazine pill" - desc = "It's magic. We don't have to explain it." //it's space magic you don't need the quantity - icon_state = "pill16" + desc = "It's magic. We don't have to explain it." + icon_state = "pillA" /obj/item/weapon/reagent_containers/pill/adminordrazine/Initialize() . = ..() - reagents.add_reagent("adminordrazine", 50) + reagents.add_reagent("adminordrazine", 5) + /obj/item/weapon/reagent_containers/pill/stox - name = "Sleeping pill" - desc = "Commonly used to treat insomnia. Contains 15 units of Soporific." - icon_state = "pill8" + name = "Soporific (15u)" + desc = "Commonly used to treat insomnia." + icon_state = "pill2" /obj/item/weapon/reagent_containers/pill/stox/Initialize() . = ..() reagents.add_reagent("stoxin", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/kelotane - name = "Kelotane pill" - desc = "Used to treat burns. Contains 15 units of Kelotane." - icon_state = "pill11" + name = "Kelotane (15u)" + desc = "Used to treat burns." + icon_state = "pill3" /obj/item/weapon/reagent_containers/pill/kelotane/Initialize() . = ..() reagents.add_reagent("kelotane", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/paracetamol - name = "Paracetamol pill" - desc = "Paracetamol! A painkiller for the ages. Chewables! Contains 15 units of Paracetamol." - icon_state = "pill8" + name = "Paracetamol (15u)" + desc = "Paracetamol! A painkiller for the ages. Chewables!" + icon_state = "pill3" /obj/item/weapon/reagent_containers/pill/paracetamol/Initialize() . = ..() reagents.add_reagent("paracetamol", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/tramadol - name = "Tramadol pill" - desc = "A simple painkiller. Contains 15 units of Tramadol." - icon_state = "pill8" + name = "Tramadol (15u)" + desc = "A simple painkiller." + icon_state = "pill3" /obj/item/weapon/reagent_containers/pill/tramadol/Initialize() . = ..() reagents.add_reagent("tramadol", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/methylphenidate - name = "Methylphenidate pill" - desc = "Improves the ability to concentrate. Contains 15 units of Methylphenidate." - icon_state = "pill8" + name = "Methylphenidate (15u)" + desc = "Improves the ability to concentrate." + icon_state = "pill2" /obj/item/weapon/reagent_containers/pill/methylphenidate/Initialize() . = ..() reagents.add_reagent("methylphenidate", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/citalopram - name = "Citalopram pill" - desc = "Mild anti-depressant. Contains 15 units of Citalopram." - icon_state = "pill8" + name = "Citalopram (15u)" + desc = "Mild anti-depressant." + icon_state = "pill4" /obj/item/weapon/reagent_containers/pill/citalopram/Initialize() . = ..() reagents.add_reagent("citalopram", 15) + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/dexalin - name = "Dexalin pill" - desc = "Used to treat oxygen deprivation. Contains 15 units of Dexalin." - icon_state = "pill16" + name = "Dexalin (15u)" + desc = "Used to treat oxygen deprivation." + icon_state = "pill1" /obj/item/weapon/reagent_containers/pill/dexalin/Initialize() . = ..() reagents.add_reagent("dexalin", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/dexalin_plus - name = "Dexalin Plus pill" - desc = "Used to treat extreme oxygen deprivation. Contains 15 units of Dexalin Plus." - icon_state = "pill8" + name = "Dexalin Plus (15u)" + desc = "Used to treat extreme oxygen deprivation." + icon_state = "pill2" /obj/item/weapon/reagent_containers/pill/dexalin_plus/Initialize() . = ..() reagents.add_reagent("dexalinp", 15) + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/dermaline - name = "Dermaline pill" - desc = "Used to treat burn wounds. Contains 15 units of Dermaline." - icon_state = "pill12" + name = "Dermaline (15u)" + desc = "Used to treat burn wounds." + icon_state = "pill2" /obj/item/weapon/reagent_containers/pill/dermaline/Initialize() . = ..() reagents.add_reagent("dermaline", 15) + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/dylovene - name = "Dylovene pill" - desc = "A broad-spectrum anti-toxin. Contains 15 units of Dylovene." - icon_state = "pill13" + name = "Dylovene (15u)" + desc = "A broad-spectrum anti-toxin." + icon_state = "pill1" /obj/item/weapon/reagent_containers/pill/dylovene/Initialize() . = ..() reagents.add_reagent("anti_toxin", 15) + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/inaprovaline - name = "Inaprovaline pill" - desc = "Used to stabilize patients. Contains 30 units of Inaprovaline." - icon_state = "pill20" + name = "Inaprovaline (30u)" + desc = "Used to stabilize patients." + icon_state = "pill2" /obj/item/weapon/reagent_containers/pill/inaprovaline/Initialize() . = ..() reagents.add_reagent("inaprovaline", 30) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/bicaridine - name = "Bicaridine pill" - desc = "Used to treat physical injuries. Contains 20 units of Bicaridine." - icon_state = "pill18" + name = "Bicaridine (20u)" + desc = "Used to treat physical injuries." + icon_state = "pill2" /obj/item/weapon/reagent_containers/pill/bicaridine/Initialize() . = ..() reagents.add_reagent("bicaridine", 20) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/spaceacillin - name = "Spaceacillin pill" - desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space. Contains 15 units of Spaceacillin." - icon_state = "pill19" + name = "Spaceacillin (10u)" + desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space." + icon_state = "pill3" /obj/item/weapon/reagent_containers/pill/spaceacillin/Initialize() . = ..() reagents.add_reagent("spaceacillin", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/carbon - name = "Carbon pill" - desc = "Used to neutralise chemicals in the stomach. Contains 15 units of Carbon." - icon_state = "pill7" + name = "Carbon (15u)" + desc = "Used to neutralise chemicals in the stomach." + icon_state = "pill3" /obj/item/weapon/reagent_containers/pill/carbon/Initialize() . = ..() reagents.add_reagent("carbon", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/iron - name = "Iron pill" - desc = "Used to aid in blood regeneration after bleeding. Contains 15 units of Iron." - icon_state = "pill4" + name = "Iron (15u)" + desc = "Used to aid in blood regeneration after bleeding." + icon_state = "pill1" /obj/item/weapon/reagent_containers/pill/iron/Initialize() . = ..() reagents.add_reagent("iron", 15) + color = reagents.get_color() //Not-quite-medicine /obj/item/weapon/reagent_containers/pill/happy name = "Happy pill" - desc = "Happy happy joy joy!" //we're not giving quantities for shady maint drugs - icon_state = "pill18" + desc = "Happy happy joy joy!" + icon_state = "pill4" /obj/item/weapon/reagent_containers/pill/happy/Initialize() . = ..() reagents.add_reagent("space_drugs", 15) reagents.add_reagent("sugar", 15) - + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/zoom name = "Zoom pill" desc = "Zoooom!" - icon_state = "pill18" + icon_state = "pill4" /obj/item/weapon/reagent_containers/pill/zoom/Initialize() . = ..() - reagents.add_reagent("impedrezene", 10) - reagents.add_reagent("synaptizine", 5) - reagents.add_reagent("hyperzine", 5) + if(prob(50)) //VOREStation edit begin: Zoom pill adjustments + reagents.add_reagent("mold", 2) //Chance to be more dangerous + reagents.add_reagent("expired_medicine", 5) + reagents.add_reagent("stimm", 5) //VOREStation edit end: Zoom pill adjustments + color = reagents.get_color() /obj/item/weapon/reagent_containers/pill/diet name = "diet pill" desc = "Guaranteed to get you slim!" - icon_state = "pill9" + icon_state = "pill4" /obj/item/weapon/reagent_containers/pill/diet/Initialize() . = ..() reagents.add_reagent("lipozine", 15) //VOREStation Edit + color = reagents.get_color() diff --git a/code/modules/reagents/reagent_containers/pill_vr.dm b/code/modules/reagents/reagent_containers/pill_vr.dm index 354a13f70a..30d084570f 100644 --- a/code/modules/reagents/reagent_containers/pill_vr.dm +++ b/code/modules/reagents/reagent_containers/pill_vr.dm @@ -1,8 +1,126 @@ /obj/item/weapon/reagent_containers/pill/nutriment name = "Nutriment pill" desc = "Used to feed people on the field. Contains 30 units of Nutriment." - icon_state = "pill6" + icon_state = "pill10" -/obj/item/weapon/reagent_containers/pill/nutriment/New() +/obj/item/weapon/reagent_containers/pill/nutriment/Initialize() ..() reagents.add_reagent("nutriment", 30) + +/obj/item/weapon/reagent_containers/pill/protein + name = "Meat pill" + desc = "Used to feed carnivores on the field. Contains 30 units of Protein." + icon_state = "pill24" + +/obj/item/weapon/reagent_containers/pill/protein/Initialize() + ..() + reagents.add_reagent("protein", 30) + +/obj/item/weapon/reagent_containers/pill/rezadone + name = "Rezadone pill" + desc = "A powder with almost magical properties, this substance can effectively treat genetic damage in humanoids, though excessive consumption has side effects." + icon_state = "pill2" + +/obj/item/weapon/reagent_containers/pill/rezadone/Initialize() + ..() + reagents.add_reagent("rezadone", 5) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/peridaxon + name = "Peridaxon pill" + desc = "Used to encourage recovery of internal organs and nervous systems. Medicate cautiously." + icon_state = "pill10" + +/obj/item/weapon/reagent_containers/pill/peridaxon/Initialize() + ..() + reagents.add_reagent("peridaxon", 10) + +/obj/item/weapon/reagent_containers/pill/carthatoline + name = "Carthatoline pill" + desc = "Carthatoline is strong evacuant used to treat severe poisoning." + icon_state = "pill4" + +/obj/item/weapon/reagent_containers/pill/carthatoline/Initialize() + ..() + reagents.add_reagent("carthatoline", 10) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/alkysine + name = "Alkysine pill" + desc = "Alkysine is a drug used to lessen the damage to neurological tissue after a catastrophic injury. Can heal brain tissue." + icon_state = "pill3" + +/obj/item/weapon/reagent_containers/pill/alkysine/Initialize() + ..() + reagents.add_reagent("alkysine", 10) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/imidazoline + name = "Imidazoline pill" + desc = "Heals eye damage." + icon_state = "pill3" + +/obj/item/weapon/reagent_containers/pill/imidazoline/Initialize() + ..() + reagents.add_reagent("imidazoline", 15) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/osteodaxon + name = "Osteodaxon pill" + desc = "An experimental drug used to heal bone fractures." + icon_state = "pill2" + +/obj/item/weapon/reagent_containers/pill/osteodaxon/Initialize() + ..() + reagents.add_reagent("osteodaxon", 10) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/myelamine + name = "Myelamine pill" + desc = "Used to rapidly clot internal hemorrhages by increasing the effectiveness of platelets." + icon_state = "pill1" + +/obj/item/weapon/reagent_containers/pill/myelamine/Initialize() + ..() + reagents.add_reagent("myelamine", 10) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/hyronalin + name = "Hyronalin pill" + desc = "Hyronalin is a medicinal drug used to counter the effect of radiation poisoning." + icon_state = "pill4" + +/obj/item/weapon/reagent_containers/pill/hyronalin/Initialize() + ..() + reagents.add_reagent("hyronalin", 15) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/arithrazine + name = "Arithrazine pill" + desc = "Arithrazine is an unstable medication used for the most extreme cases of radiation poisoning." + icon_state = "pill2" + +/obj/item/weapon/reagent_containers/pill/arithrazine/Initialize() + ..() + reagents.add_reagent("arithrazine", 5) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/corophizine + name = "Corophizine pill" + desc = "A wide-spectrum antibiotic drug. Powerful and uncomfortable in equal doses." + icon_state = "pill2" + +/obj/item/weapon/reagent_containers/pill/corophizine/Initialize() + ..() + reagents.add_reagent("corophizine", 5) + color = reagents.get_color() + +/obj/item/weapon/reagent_containers/pill/healing_nanites + name = "Healing nanites capsule" + desc = "Miniature medical robots that swiftly restore bodily damage." + icon_state = "pill1" + +/obj/item/weapon/reagent_containers/pill/healing_nanites/Initialize() + ..() + reagents.add_reagent("healing_nanites", 30) + color = reagents.get_color() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index d2b4dc149d..4ccd444d0e 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -158,7 +158,7 @@ message_admins("[key_name_admin(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP).") log_game("[key_name(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).") - if(!istype(Proj ,/obj/item/projectile/beam/lastertag) && !istype(Proj ,/obj/item/projectile/beam/practice) ) + if(!istype(Proj ,/obj/item/projectile/beam/lasertag) && !istype(Proj ,/obj/item/projectile/beam/practice) ) explode() /obj/structure/reagent_dispensers/fueltank/ex_act() diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 98d3500c6e..8a2e9796cd 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -136,7 +136,7 @@ if(isrobot(user)) return - if(!I) + if(!I || I.anchored || !I.canremove) return user.drop_item() diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index b60b71cc4e..69b9937158 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -1,7 +1,7 @@ /obj/structure/bigDelivery desc = "A big wrapped package." name = "large parcel" - icon = 'icons/obj/storage.dmi' + icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit icon_state = "deliverycloset" var/obj/wrapped = null density = 1 @@ -109,7 +109,7 @@ /obj/item/smallDelivery desc = "A small wrapped package." name = "small parcel" - icon = 'icons/obj/storage.dmi' + icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit icon_state = "deliverycrate3" var/obj/item/wrapped = null var/sortTag = null diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 106c655ae2..938495b00f 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -61,22 +61,4 @@ other types of metals and chemistry for reagents). return new build_path(newloc) /datum/design/item - build_type = PROTOLATHE - -/datum/design/item/design_disk - name = "Design Storage Disk" - desc = "Produce additional disks for storing device designs." - id = "design_disk" - req_tech = list(TECH_DATA = 1) - materials = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 10) - build_path = /obj/item/weapon/disk/design_disk - sort_string = "GAAAA" - -/datum/design/item/tech_disk - name = "Technology Data Storage Disk" - desc = "Produce additional disks for storing technology data." - id = "tech_disk" - req_tech = list(TECH_DATA = 1) - materials = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 10) - build_path = /obj/item/weapon/disk/tech_disk - sort_string = "GAAAB" \ No newline at end of file + build_type = PROTOLATHE \ No newline at end of file diff --git a/code/modules/research/designs/HUDs.dm b/code/modules/research/designs/HUDs.dm new file mode 100644 index 0000000000..4a6648f26a --- /dev/null +++ b/code/modules/research/designs/HUDs.dm @@ -0,0 +1,47 @@ +// HUDs + +/datum/design/item/hud + materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) + +/datum/design/item/hud/AssembleDesignName() + ..() + name = "HUD glasses prototype ([item_name])" + +/datum/design/item/hud/AssembleDesignDesc() + desc = "Allows for the construction of \a [item_name] HUD glasses." + +/datum/design/item/hud/health + name = "health scanner" + id = "health_hud" + req_tech = list(TECH_BIO = 2, TECH_MAGNET = 3) + build_path = /obj/item/clothing/glasses/hud/health + sort_string = "EAAAA" + +/datum/design/item/hud/security + name = "security records" + id = "security_hud" + req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 2) + build_path = /obj/item/clothing/glasses/hud/security + sort_string = "EAAAB" + +/datum/design/item/hud/mesons + name = "optical meson scanner" + id = "mesons" + req_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) + build_path = /obj/item/clothing/glasses/meson + sort_string = "EAAAC" + +/datum/design/item/hud/material + name = "optical material scanner" + id = "material" + req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3) + build_path = /obj/item/clothing/glasses/material + sort_string = "EAAAD" + +/datum/design/item/hud/graviton_visor + name = "graviton visor" + id = "graviton_goggles" + req_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 3, TECH_BLUESPACE = 3, TECH_PHORON = 3) + materials = list(MAT_PLASTEEL = 2000, "glass" = 3000, MAT_PHORON = 1500) + build_path = /obj/item/clothing/glasses/graviton + sort_string = "EAAAE" \ No newline at end of file diff --git a/code/modules/research/designs/HUDs_vr.dm b/code/modules/research/designs/HUDs_vr.dm new file mode 100644 index 0000000000..aef8b12b0c --- /dev/null +++ b/code/modules/research/designs/HUDs_vr.dm @@ -0,0 +1,7 @@ +/datum/design/item/hud/omni + name = "AR glasses" + id = "omnihud" + req_tech = list(TECH_MAGNET = 4, TECH_COMBAT = 3, TECH_BIO = 3) + materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 1000) + build_path = /obj/item/clothing/glasses/omnihud + sort_string = "EAAVA" \ No newline at end of file diff --git a/code/modules/research/designs/ai_holders.dm b/code/modules/research/designs/ai_holders.dm new file mode 100644 index 0000000000..8cdcafbd6c --- /dev/null +++ b/code/modules/research/designs/ai_holders.dm @@ -0,0 +1,51 @@ +// Various AI/mind holding device +/datum/design/item/ai_holder/AssembleDesignName() + ..() + name = "Mind storage device prototype ([item_name])" + +/datum/design/item/ai_holder/mmi + name = "Man-machine interface" + id = "mmi" + req_tech = list(TECH_DATA = 2, TECH_BIO = 3) + build_type = PROTOLATHE | PROSFAB + materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500) + build_path = /obj/item/device/mmi + category = "Misc" + sort_string = "SAAAA" + +/datum/design/item/ai_holder/posibrain + name = "Positronic brain" + id = "posibrain" + req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 6, TECH_BLUESPACE = 2, TECH_DATA = 4) + build_type = PROTOLATHE | PROSFAB + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 500, "phoron" = 500, "diamond" = 100) + build_path = /obj/item/device/mmi/digital/posibrain + category = "Misc" + sort_string = "SAAAB" + +/datum/design/item/ai_holder/dronebrain + name = "Robotic intelligence circuit" + id = "dronebrain" + req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_DATA = 4) + build_type = PROTOLATHE | PROSFAB + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 500) + build_path = /obj/item/device/mmi/digital/robot + category = "Misc" + sort_string = "SAAAC" + +/datum/design/item/ai_holder/paicard + name = "'pAI', personal artificial intelligence device" + id = "paicard" + req_tech = list(TECH_DATA = 2) + materials = list("glass" = 500, DEFAULT_WALL_MATERIAL = 500) + build_path = /obj/item/device/paicard + sort_string = "SBAAA" + +/datum/design/item/ai_holder/intellicard + name = "intelliCore" + desc = "Allows for the construction of an intelliCore." + id = "intellicore" + req_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) + materials = list("glass" = 1000, "gold" = 200) + build_path = /obj/item/device/aicard + sort_string = "SCAAA" \ No newline at end of file diff --git a/code/modules/research/designs/bag_of_holding.dm b/code/modules/research/designs/bag_of_holding.dm new file mode 100644 index 0000000000..744bbca5a3 --- /dev/null +++ b/code/modules/research/designs/bag_of_holding.dm @@ -0,0 +1,23 @@ +// Bags of holding + +/datum/design/item/boh/AssembleDesignName() + ..() + name = "Infinite capacity storage prototype ([item_name])" + +/datum/design/item/boh/bag_holding + name = "Bag of Holding" + desc = "Using localized pockets of bluespace this bag prototype offers incredible storage capacity with the contents weighting nothing. It's a shame the bag itself is pretty heavy." + id = "bag_holding" + req_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 6) + materials = list("gold" = 3000, "diamond" = 1500, "uranium" = 250) + build_path = /obj/item/weapon/storage/backpack/holding + sort_string = "QAAAA" + +/datum/design/item/boh/dufflebag_holding + name = "DuffleBag of Holding" + desc = "A minaturized prototype of the popular Bag of Holding, the Dufflebag of Holding is, functionally, identical to the bag of holding, but comes in a more stylish and compact form." + id = "dufflebag_holding" + req_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 6) + materials = list("gold" = 3000, "diamond" = 1500, "uranium" = 250) + build_path = /obj/item/weapon/storage/backpack/holding/duffle + sort_string = "QAAAB" \ No newline at end of file diff --git a/code/modules/research/designs/beakers.dm b/code/modules/research/designs/beakers.dm new file mode 100644 index 0000000000..fe231699d5 --- /dev/null +++ b/code/modules/research/designs/beakers.dm @@ -0,0 +1,22 @@ +// Various beakers + +/datum/design/item/beaker/AssembleDesignName() + name = "Beaker prototype ([item_name])" + +/datum/design/item/beaker/noreact + name = "cryostasis" + desc = "A cryostasis beaker that allows for chemical storage without reactions. Can hold up to 50 units." + id = "splitbeaker" + req_tech = list(TECH_MATERIAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 3000) + build_path = /obj/item/weapon/reagent_containers/glass/beaker/noreact + sort_string = "IAAAA" + +/datum/design/item/beaker/bluespace + name = TECH_BLUESPACE + desc = "A bluespace beaker, powered by experimental bluespace technology and Element Cuban combined with the Compound Pete. Can hold up to 300 units." + id = "bluespacebeaker" + req_tech = list(TECH_BLUESPACE = 2, TECH_MATERIAL = 6) + materials = list(DEFAULT_WALL_MATERIAL = 3000, "phoron" = 3000, "diamond" = 500) + build_path = /obj/item/weapon/reagent_containers/glass/beaker/bluespace + sort_string = "IAAAB" \ No newline at end of file diff --git a/code/modules/research/designs/bio_devices.dm b/code/modules/research/designs/bio_devices.dm new file mode 100644 index 0000000000..8762364105 --- /dev/null +++ b/code/modules/research/designs/bio_devices.dm @@ -0,0 +1,61 @@ +/datum/design/item/biotech + materials = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 20) + +/datum/design/item/biotech/AssembleDesignName() + ..() + name = "Biotech device prototype ([item_name])" + +// Biotech of various types + +/datum/design/item/biotech/mass_spectrometer + desc = "A device for analyzing chemicals in blood." + id = "mass_spectrometer" + req_tech = list(TECH_BIO = 2, TECH_MAGNET = 2) + build_path = /obj/item/device/mass_spectrometer + sort_string = "JAAAA" + +/datum/design/item/biotech/adv_mass_spectrometer + desc = "A device for analyzing chemicals in blood and their quantities." + id = "adv_mass_spectrometer" + req_tech = list(TECH_BIO = 2, TECH_MAGNET = 4) + build_path = /obj/item/device/mass_spectrometer/adv + sort_string = "JAAAB" + +/datum/design/item/biotech/reagent_scanner + desc = "A device for identifying chemicals." + id = "reagent_scanner" + req_tech = list(TECH_BIO = 2, TECH_MAGNET = 2) + build_path = /obj/item/device/reagent_scanner + sort_string = "JAABA" + +/datum/design/item/biotech/adv_reagent_scanner + desc = "A device for identifying chemicals and their proportions." + id = "adv_reagent_scanner" + req_tech = list(TECH_BIO = 2, TECH_MAGNET = 4) + build_path = /obj/item/device/reagent_scanner/adv + sort_string = "JAABB" + +/datum/design/item/biotech/robot_scanner + desc = "A hand-held scanner able to diagnose robotic injuries." + id = "robot_scanner" + req_tech = list(TECH_MAGNET = 3, TECH_BIO = 2, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 200) + build_path = /obj/item/device/robotanalyzer + sort_string = "JAACA" + +/datum/design/item/biotech/nanopaste + desc = "A tube of paste containing swarms of repair nanites. Very effective in repairing robotic machinery." + id = "nanopaste" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 7000) + build_path = /obj/item/stack/nanopaste + sort_string = "JAACB" + +/datum/design/item/biotech/plant_analyzer + desc = "A device capable of quickly scanning all relevant data about a plant." + id = "plant_analyzer" + req_tech = list(TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 500) + build_path = /obj/item/device/analyzer/plant_analyzer + sort_string = "JAADA" + diff --git a/code/modules/research/designs/bio_devices_vr.dm b/code/modules/research/designs/bio_devices_vr.dm new file mode 100644 index 0000000000..2deb1854f9 --- /dev/null +++ b/code/modules/research/designs/bio_devices_vr.dm @@ -0,0 +1,23 @@ +/datum/design/item/biotech/nif + name = "nanite implant framework" + id = "nif" + req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 8000, "uranium" = 6000, "diamond" = 6000) + build_path = /obj/item/device/nif + sort_string = "JVAAA" + +/datum/design/item/biotech/nifbio + name = "bioadaptive NIF" + id = "bioadapnif" + req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 15000, "uranium" = 10000, "diamond" = 10000) + build_path = /obj/item/device/nif/bioadap + sort_string = "JVAAB" + +/datum/design/item/biotech/nifrepairtool + name = "adv. NIF repair tool" + id = "anrt" + req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5) + materials = list(DEFAULT_WALL_MATERIAL = 200, "glass" = 3000, "uranium" = 2000, "diamond" = 2000) + build_path = /obj/item/device/nifrepairer + sort_string = "JVABA" \ No newline at end of file diff --git a/code/modules/research/designs/circuit_assembly.dm b/code/modules/research/designs/circuit_assembly.dm index 9f20032445..0ce5d0ca8a 100644 --- a/code/modules/research/designs/circuit_assembly.dm +++ b/code/modules/research/designs/circuit_assembly.dm @@ -1,89 +1,99 @@ -/datum/design/item/wirer - name = "Custom wirer tool" - id = "wirer" - req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 2500) - build_path = /obj/item/device/integrated_electronics/wirer - sort_string = "VBVAA" +// Integrated circuits stuff -/datum/design/item/debugger - name = "Custom circuit debugger tool" - id = "debugger" - req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 2500) - build_path = /obj/item/device/integrated_electronics/debugger - sort_string = "VBVAB" +/datum/design/item/integrated_circuitry/AssembleDesignName() + ..() + name = "Circuitry device design ([item_name])" - - -/datum/design/item/custom_circuit_assembly - name = "Small custom assembly" - desc = "A customizable assembly for simple, small devices." - id = "assembly-small" - req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 10000) - build_path = /obj/item/device/electronic_assembly - sort_string = "VCAAA" - -/datum/design/item/custom_circuit_assembly/medium - name = "Medium custom assembly" - desc = "A customizable assembly suited for more ambitious mechanisms." - id = "assembly-medium" - req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_POWER = 3) - materials = list(DEFAULT_WALL_MATERIAL = 20000) - build_path = /obj/item/device/electronic_assembly/medium - sort_string = "VCAAB" - -/datum/design/item/custom_circuit_assembly/drone - name = "Drone custom assembly" - desc = "A customizable assembly optimized for autonomous devices." - id = "assembly-drone" - req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4, TECH_POWER = 4) - materials = list(DEFAULT_WALL_MATERIAL = 30000) - build_path = /obj/item/device/electronic_assembly/drone - sort_string = "VCAAC" - -/datum/design/item/custom_circuit_assembly/large - name = "Large custom assembly" - desc = "A customizable assembly for large machines." - id = "assembly-large" - req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_POWER = 4) - materials = list(DEFAULT_WALL_MATERIAL = 40000) - build_path = /obj/item/device/electronic_assembly/large - sort_string = "VCAAD" - -/datum/design/item/custom_circuit_assembly/implant - name = "Implant custom assembly" - desc = "An customizable assembly for very small devices, implanted into living entities." - id = "assembly-implant" - req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_POWER = 3, TECH_BIO = 5) - materials = list(DEFAULT_WALL_MATERIAL = 2000) - build_path = /obj/item/weapon/implant/integrated_circuit - sort_string = "VCAAE" - -/datum/design/item/custom_circuit_assembly/device - name = "Device custom assembly" - desc = "An customizable assembly designed to interface with other devices." - id = "assembly-device" - req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 5000) - build_path = /obj/item/device/assembly/electronic_assembly - sort_string = "VCAAF" - -/datum/design/item/custom_circuit_printer +/datum/design/item/integrated_circuitry/custom_circuit_printer name = "Portable integrated circuit printer" desc = "A portable(ish) printer for modular machines." id = "ic_printer" req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 4, TECH_DATA = 5) materials = list(DEFAULT_WALL_MATERIAL = 10000) build_path = /obj/item/device/integrated_circuit_printer - sort_string = "VCAAG" + sort_string = "UAAAA" -/datum/design/item/custom_circuit_printer_upgrade +/datum/design/item/integrated_circuitry/custom_circuit_printer_upgrade name = "Integrated circuit printer upgrade - advanced designs" desc = "Allows the integrated circuit printer to create advanced circuits" id = "ic_printer_upgrade_adv" req_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4) materials = list(DEFAULT_WALL_MATERIAL = 2000) build_path = /obj/item/weapon/disk/integrated_circuit/upgrade/advanced - sort_string = "VCAAH" \ No newline at end of file + sort_string = "UBAAA" + +/datum/design/item/integrated_circuitry/wirer + name = "Custom wirer tool" + id = "wirer" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 2500) + build_path = /obj/item/device/integrated_electronics/wirer + sort_string = "UCAAA" + +/datum/design/item/integrated_circuitry/debugger + name = "Custom circuit debugger tool" + id = "debugger" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 2500) + build_path = /obj/item/device/integrated_electronics/debugger + sort_string = "UCBBB" + +// Assemblies + +/datum/design/item/integrated_circuitry/assembly/AssembleDesignName() + ..() + name = "Circuitry assembly design ([item_name])" + +/datum/design/item/integrated_circuitry/assembly/custom_circuit_assembly_small + name = "Small custom assembly" + desc = "A customizable assembly for simple, small devices." + id = "assembly-small" + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 10000) + build_path = /obj/item/device/electronic_assembly + sort_string = "UDAAA" + +/datum/design/item/integrated_circuitry/assembly/custom_circuit_assembly_medium + name = "Medium custom assembly" + desc = "A customizable assembly suited for more ambitious mechanisms." + id = "assembly-medium" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_POWER = 3) + materials = list(DEFAULT_WALL_MATERIAL = 20000) + build_path = /obj/item/device/electronic_assembly/medium + sort_string = "UDAAB" + +/datum/design/item/integrated_circuitry/assembly/custom_circuit_assembly_large + name = "Large custom assembly" + desc = "A customizable assembly for large machines." + id = "assembly-large" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_POWER = 4) + materials = list(DEFAULT_WALL_MATERIAL = 40000) + build_path = /obj/item/device/electronic_assembly/large + sort_string = "UDAAC" + +/datum/design/item/integrated_circuitry/assembly/custom_circuit_assembly_drone + name = "Drone custom assembly" + desc = "A customizable assembly optimized for autonomous devices." + id = "assembly-drone" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4, TECH_POWER = 4) + materials = list(DEFAULT_WALL_MATERIAL = 30000) + build_path = /obj/item/device/electronic_assembly/drone + sort_string = "UDAAD" + +/datum/design/item/integrated_circuitry/assembly/custom_circuit_assembly_device + name = "Device custom assembly" + desc = "An customizable assembly designed to interface with other devices." + id = "assembly-device" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000) + build_path = /obj/item/device/assembly/electronic_assembly + sort_string = "UDAAE" + +/datum/design/item/integrated_circuitry/assembly/custom_circuit_assembly_implant + name = "Implant custom assembly" + desc = "An customizable assembly for very small devices, implanted into living entities." + id = "assembly-implant" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_POWER = 3, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 2000) + build_path = /obj/item/weapon/implant/integrated_circuit + sort_string = "UDAAF" \ No newline at end of file diff --git a/code/modules/research/designs/ai_modules.dm b/code/modules/research/designs/circuits/ai_modules.dm similarity index 89% rename from code/modules/research/designs/ai_modules.dm rename to code/modules/research/designs/circuits/ai_modules.dm index eae26ea386..378a4fde0d 100644 --- a/code/modules/research/designs/ai_modules.dm +++ b/code/modules/research/designs/circuits/ai_modules.dm @@ -104,14 +104,4 @@ id = "tyrant" req_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 2, TECH_MATERIAL = 6) build_path = /obj/item/weapon/aiModule/tyrant - sort_string = "XACAD" - -// AI file, AI tool -/datum/design/item/intellicard - name = "'intelliCore', AI preservation and transportation system" - desc = "Allows for the construction of an intelliCore." - id = "intellicore" - req_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) - materials = list("glass" = 1000, "gold" = 200) - build_path = /obj/item/device/aicard - sort_string = "VACAA" \ No newline at end of file + sort_string = "XACAD" \ No newline at end of file diff --git a/code/modules/research/designs/circuits.dm b/code/modules/research/designs/circuits/circuits.dm similarity index 96% rename from code/modules/research/designs/circuits.dm rename to code/modules/research/designs/circuits/circuits.dm index 6c79366e66..f47ff46947 100644 --- a/code/modules/research/designs/circuits.dm +++ b/code/modules/research/designs/circuits/circuits.dm @@ -287,6 +287,13 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/solar_control sort_string = "JAAAF" +/datum/design/circuit/shutoff_monitor + name = "Automatic shutoff valve monitor" + id = "shutoff_monitor" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3) + build_path = /obj/item/weapon/circuitboard/shutoff_monitor + sort_string = "JAAAG" + /datum/design/circuit/pacman name = "PACMAN-type generator" id = "pacman" @@ -468,6 +475,13 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/mecha/gygax/targeting sort_string = "NAACC" +/datum/design/circuit/mecha/gygax_medical + name = "'Serenity' medical control" + id = "gygax_medical" + req_tech = list(TECH_DATA = 4, TECH_BIO = 2) + build_path = /obj/item/weapon/circuitboard/mecha/gygax/medical + sort_string = "NAACD" + /datum/design/circuit/mecha/durand_main name = "'Durand' central control" id = "durand_main" @@ -579,6 +593,13 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/shield_cap sort_string = "VAAAC" +/datum/design/circuit/ntnet_relay + name = "NTNet Quantum Relay" + id = "ntnet_relay" + req_tech = list(TECH_DATA = 4) + build_path = /obj/item/weapon/circuitboard/ntnet_relay + sort_string = "WAAAA" + /datum/design/circuit/aicore name = "AI core" id = "aicore" @@ -594,4 +615,4 @@ CIRCUITS BELOW /datum/design/circuit/shield req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3) materials = list("$glass" = 2000, "sacid" = 20, "$phoron" = 10000, "$diamond" = 5000, "$gold" = 10000) -*/ \ No newline at end of file +*/ diff --git a/code/modules/research/designs/circuits/circuits_vr.dm b/code/modules/research/designs/circuits/circuits_vr.dm new file mode 100644 index 0000000000..fefb2b2db3 --- /dev/null +++ b/code/modules/research/designs/circuits/circuits_vr.dm @@ -0,0 +1,149 @@ +/datum/design/circuit/algae_farm + name = "Algae Oxygen Generator" + id = "algae_farm" + req_tech = list(TECH_ENGINEERING = 3, TECH_BIO = 2) + build_path = /obj/item/weapon/circuitboard/algae_farm + sort_string = "HABAE" + +/datum/design/circuit/thermoregulator + name = "thermal regulator" + id = "thermoregulator" + req_tech = list(TECH_ENGINEERING = 4, TECH_POWER = 3) + build_path = /obj/item/weapon/circuitboard/thermoregulator + sort_string = "HABAF" + +/datum/design/circuit/bomb_tester + name = "Explosive Effect Simulator" + id = "bomb_tester" + req_tech = list(TECH_PHORON = 3, TECH_DATA = 2, TECH_MAGNET = 2) + build_path = /obj/item/weapon/circuitboard/bomb_tester + sort_string = "HABAG" + +/datum/design/circuit/quantum_pad + name = "Quantum Pad" + id = "quantum_pad" + req_tech = list(TECH_ENGINEERING = 4, TECH_POWER = 4, TECH_BLUESPACE = 4) + build_path = /obj/item/weapon/circuitboard/quantumpad + sort_string = "HABAH" + +//////Micro mech stuff +/datum/design/circuit/mecha/gopher_main + name = "'Gopher' central control" + id = "gopher_main" + build_path = /obj/item/weapon/circuitboard/mecha/gopher/main + sort_string = "NAAEA" + +/datum/design/circuit/mecha/gopher_peri + name = "'Gopher' peripherals control" + id = "gopher_peri" + build_path = /obj/item/weapon/circuitboard/mecha/gopher/peripherals + sort_string = "NAAEB" + +/datum/design/circuit/mecha/polecat_main + name = "'Polecat' central control" + id = "polecat_main" + req_tech = list(TECH_DATA = 4) + build_path = /obj/item/weapon/circuitboard/mecha/polecat/main + sort_string = "NAAFA" + +/datum/design/circuit/mecha/polecat_peri + name = "'Polecat' peripherals control" + id = "polecat_peri" + req_tech = list(TECH_DATA = 4) + build_path = /obj/item/weapon/circuitboard/mecha/polecat/peripherals + sort_string = "NAAFB" + +/datum/design/circuit/mecha/polecat_targ + name = "'Polecat' weapon control and targeting" + id = "polecat_targ" + req_tech = list(TECH_DATA = 4, TECH_COMBAT = 2) + build_path = /obj/item/weapon/circuitboard/mecha/polecat/targeting + sort_string = "NAAFC" + +/datum/design/circuit/mecha/weasel_main + name = "'Weasel' central control" + id = "weasel_main" + req_tech = list(TECH_DATA = 4) + build_path = /obj/item/weapon/circuitboard/mecha/weasel/main + sort_string = "NAAGA" + +/datum/design/circuit/mecha/weasel_peri + name = "'Weasel' peripherals control" + id = "weasel_peri" + req_tech = list(TECH_DATA = 4) + build_path = /obj/item/weapon/circuitboard/mecha/weasel/peripherals + sort_string = "NAAGB" + +/datum/design/circuit/mecha/weasel_targ + name = "'Weasel' weapon control and targeting" + id = "weasel_targ" + req_tech = list(TECH_DATA = 4, TECH_COMBAT = 2) + build_path = /obj/item/weapon/circuitboard/mecha/weasel/targeting + sort_string = "NAAGC" + +/datum/design/circuit/transhuman_clonepod + name = "grower pod" + id = "transhuman_clonepod" + req_tech = list(TECH_DATA = 3, TECH_BIO = 3) + build_path = /obj/item/weapon/circuitboard/transhuman_clonepod + sort_string = "HAADA" + +/datum/design/circuit/transhuman_synthprinter + name = "SynthFab 3000" + id = "transhuman_synthprinter" + req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) + build_path = /obj/item/weapon/circuitboard/transhuman_synthprinter + sort_string = "HAADB" + +/datum/design/circuit/transhuman_resleever + name = "Resleeving pod" + id = "transhuman_resleever" + req_tech = list(TECH_ENGINEERING = 4, TECH_BIO = 4) + build_path = /obj/item/weapon/circuitboard/transhuman_resleever + sort_string = "HAADC" + +// Resleeving + +/datum/design/circuit/resleeving_control + name = "Resleeving control console" + id = "resleeving_control" + req_tech = list(TECH_DATA = 5) + build_path = /obj/item/weapon/circuitboard/resleeving_control + sort_string = "HAADE" + +/datum/design/circuit/body_designer + name = "Body design console" + id = "body_designer" + req_tech = list(TECH_DATA = 5) + build_path = /obj/item/weapon/circuitboard/body_designer + sort_string = "HAADF" + +/datum/design/circuit/partslathe + name = "Parts lathe" + id = "partslathe" + req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + build_path = /obj/item/weapon/circuitboard/partslathe + sort_string = "HABAD" + +// Telesci stuff + +/datum/design/circuit/telesci_console + name = "Telepad Control Console" + id = "telesci_console" + req_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 3, TECH_PHORON = 4) + build_path = /obj/item/weapon/circuitboard/telesci_console + sort_string = "HAAEA" + +/datum/design/circuit/telesci_pad + name = "Telepad" + id = "telesci_pad" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_PHORON = 4, TECH_BLUESPACE = 5) + build_path = /obj/item/weapon/circuitboard/telesci_pad + sort_string = "HAAEB" + +/datum/design/circuit/quantum_pad + name = "Quantum Pad" + id = "quantum_pad" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_PHORON = 4, TECH_BLUESPACE = 5) + build_path = /obj/item/weapon/circuitboard/quantumpad + sort_string = "HAAC" \ No newline at end of file diff --git a/code/modules/research/designs/engineering.dm b/code/modules/research/designs/engineering.dm new file mode 100644 index 0000000000..f499a47a86 --- /dev/null +++ b/code/modules/research/designs/engineering.dm @@ -0,0 +1,74 @@ +// Tools + +/datum/design/item/tool/AssembleDesignName() + ..() + name = "Experimental tool prototype ([item_name])" + +/datum/design/item/tool/experimental_welder + name = "Experimental welding tool" + desc = "A welding tool that generate fuel for itself." + id = "expwelder" + req_tech = list(TECH_ENGINEERING = 4, TECH_PHORON = 3, TECH_MATERIAL = 4) + materials = list(DEFAULT_WALL_MATERIAL = 70, "glass" = 120, "phoron" = 100) + build_path = /obj/item/weapon/weldingtool/experimental + sort_string = "NAAAA" + +/datum/design/item/tool/hand_drill + name = "Hand drill" + desc = "A simple powered hand drill." + id = "handdrill" + req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 300, "silver" = 100) + build_path = /obj/item/weapon/tool/screwdriver/power + sort_string = "NAAAB" + +/datum/design/item/tool/jaws_life + name = "Jaws of life" + desc = "A set of jaws of life, compressed through the magic of science." + id = "jawslife" + req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 300, "silver" = 100) + build_path = /obj/item/weapon/tool/crowbar/power + sort_string = "NAAAC" + +// Other devices + +/datum/design/item/engineering/AssembleDesignName() + ..() + name = "Engineering device prototype ([item_name])" + +/datum/design/item/engineering/t_scanner + name = "T-ray Scanner" + desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + id = "tscanner" + req_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2, TECH_MATERIAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 200) + build_path = /obj/item/device/t_scanner + sort_string = "NBAAA" + +/datum/design/item/engineering/t_scanner_upg + name = "Upgraded T-ray Scanner" + desc = "An upgraded version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + id = "upgradedtscanner" + req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 4, TECH_MATERIAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500, "phoron" = 150) + build_path = /obj/item/device/t_scanner/upgraded + sort_string = "NBAAB" + +/datum/design/item/engineering/t_scanner_adv + name = "Advanced T-ray Scanner" + desc = "An advanced version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + id = "advancedtscanner" + req_tech = list(TECH_MAGNET = 6, TECH_ENGINEERING = 6, TECH_MATERIAL = 6) + materials = list(DEFAULT_WALL_MATERIAL = 1250, "phoron" = 500, "silver" = 50) + build_path = /obj/item/device/t_scanner/advanced + sort_string = "NBAAC" + +/datum/design/item/engineering/atmosanalyzer + name = "Analyzer" + desc = "A hand-held environmental scanner which reports current gas levels." + id = "atmosanalyzer" + req_tech = list(TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 200, "glass" = 100) + build_path = /obj/item/device/analyzer + sort_string = "NBABA" \ No newline at end of file diff --git a/code/modules/research/designs/illegal.dm b/code/modules/research/designs/illegal.dm deleted file mode 100644 index c195b5764b..0000000000 --- a/code/modules/research/designs/illegal.dm +++ /dev/null @@ -1,26 +0,0 @@ -// Yeah yeah, vague file name. Basically a misc folder for antag things that RnD can make. - -/datum/design/item/binaryencrypt - name = "Binary encryption key" - desc = "Allows for deciphering the binary channel on-the-fly." - id = "binaryencrypt" - req_tech = list(TECH_ILLEGAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 300, "glass" = 300) - build_path = /obj/item/device/encryptionkey/binary - sort_string = "VASAA" - -/datum/design/item/chameleon - name = "Holographic equipment kit" - desc = "A kit of dangerous, high-tech equipment with changeable looks." - id = "chameleon" - req_tech = list(TECH_ILLEGAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 500) - build_path = /obj/item/weapon/storage/box/syndie_kit/chameleon - sort_string = "VASBA" - -/datum/design/item/weapon/esword - id = "chargesword" - req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 4, TECH_ENGINEERING = 5, TECH_ILLEGAL = 4, TECH_ANOMALY = 1) - materials = list(MAT_PLASTEEL = 3500, "glass" = 1000, MAT_LEAD = 2250, MAT_METALHYDROGEN = 500) - build_path = /obj/item/weapon/melee/energy/sword/charge - sort_string = "VASCA" diff --git a/code/modules/research/designs/implants.dm b/code/modules/research/designs/implants.dm new file mode 100644 index 0000000000..4c4b5ba801 --- /dev/null +++ b/code/modules/research/designs/implants.dm @@ -0,0 +1,22 @@ +// Implants + +/datum/design/item/implant + materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) + +/datum/design/item/implant/AssembleDesignName() + ..() + name = "Implantable biocircuit design ([item_name])" + +/datum/design/item/implant/chemical + name = "chemical" + id = "implant_chem" + req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3) + build_path = /obj/item/weapon/implantcase/chem + sort_string = "MFAAA" + +/datum/design/item/implant/freedom + name = "freedom" + id = "implant_free" + req_tech = list(TECH_ILLEGAL = 2, TECH_BIO = 3) + build_path = /obj/item/weapon/implantcase/freedom + sort_string = "MFAAB" \ No newline at end of file diff --git a/code/modules/research/designs/implants_vr.dm b/code/modules/research/designs/implants_vr.dm new file mode 100644 index 0000000000..07b4a37f05 --- /dev/null +++ b/code/modules/research/designs/implants_vr.dm @@ -0,0 +1,25 @@ +/datum/design/item/implant/backup + name = "Backup implant" + id = "implant_backup" + req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2, TECH_DATA = 4, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 2000) + build_path = /obj/item/weapon/implantcase/backup + sort_string = "MFAVA" + +/datum/design/item/implant/sizecontrol + name = "Size control implant" + id = "implant_size" + req_tech = list(TECH_MATERIAL = 3, TECH_BIO = 4, TECH_DATA = 4, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 2000, "silver" = 3000) + build_path = /obj/item/weapon/implanter/sizecontrol + sort_string = "MFAVB" + +/* Make language great again +/datum/design/item/implant/language + name = "Language implant" + id = "implant_language" + req_tech = list(TECH_MATERIAL = 5, TECH_BIO = 5, TECH_DATA = 4, TECH_ENGINEERING = 4) //This is not an easy to make implant. + materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 7000, "gold" = 2000, "diamond" = 3000) + build_path = /obj/item/weapon/implantcase/vrlanguage + sort_string = "MFAVC" +*/ \ No newline at end of file diff --git a/code/modules/research/designs/locator_devices.dm b/code/modules/research/designs/locator_devices.dm new file mode 100644 index 0000000000..b6f149393c --- /dev/null +++ b/code/modules/research/designs/locator_devices.dm @@ -0,0 +1,80 @@ +// GPS + +/datum/design/item/gps + req_tech = list(TECH_MATERIAL = 2, TECH_DATA = 2, TECH_BLUESPACE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500) + +/datum/design/item/gps/AssembleDesignName() + ..() + name = "Triangulating device design ([name])" + +/datum/design/item/gps/generic + name = "GEN" + id = "gps_gen" + build_path = /obj/item/device/gps + sort_string = "DAAAA" + +/datum/design/item/gps/command + name = "COM" + id = "gps_com" + build_path = /obj/item/device/gps/command + sort_string = "DAAAB" + +/datum/design/item/gps/security + name = "SEC" + id = "gps_sec" + build_path = /obj/item/device/gps/security + sort_string = "DAAAC" + +/datum/design/item/gps/medical + name = "MED" + id = "gps_med" + build_path = /obj/item/device/gps/medical + sort_string = "DAAAD" + +/datum/design/item/gps/engineering + name = "ENG" + id = "gps_eng" + build_path = /obj/item/device/gps/engineering + sort_string = "DAAAE" + +/datum/design/item/gps/science + name = "SCI" + id = "gps_sci" + build_path = /obj/item/device/gps/science + sort_string = "DAAAF" + +/datum/design/item/gps/mining + name = "MINE" + id = "gps_mine" + build_path = /obj/item/device/gps/mining + sort_string = "DAAAG" + +/datum/design/item/gps/explorer + name = "EXP" + id = "gps_exp" + build_path = /obj/item/device/gps/explorer + sort_string = "DAAAH" + +// Other locators + +/datum/design/item/locator/AssembleDesignName() + ..() + name = "Locator device design ([name])" + +/datum/design/item/locator/beacon_locator + name = "Tracking beacon pinpointer" + desc = "Used to scan and locate signals on a particular frequency." + id = "beacon_locator" + req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 2, TECH_BLUESPACE = 3) + materials = list(DEFAULT_WALL_MATERIAL = 1000,"glass" = 500) + build_path = /obj/item/device/beacon_locator + sort_string = "DBAAA" + +/datum/design/item/locator/beacon + name = "Bluespace tracking beacon" + id = "beacon" + req_tech = list(TECH_BLUESPACE = 1) + materials = list (DEFAULT_WALL_MATERIAL = 20, "glass" = 10) + build_path = /obj/item/device/radio/beacon + sort_string = "DBABA" \ No newline at end of file diff --git a/code/modules/research/designs/medical.dm b/code/modules/research/designs/medical.dm index 8e821fe0fe..7fcc4ab1f6 100644 --- a/code/modules/research/designs/medical.dm +++ b/code/modules/research/designs/medical.dm @@ -3,72 +3,9 @@ /datum/design/item/medical/AssembleDesignName() ..() - name = "Biotech device prototype ([item_name])" + name = "Medical equipment prototype ([item_name])" -/datum/design/item/medical/robot_scanner - desc = "A hand-held scanner able to diagnose robotic injuries." - id = "robot_scanner" - req_tech = list(TECH_MAGNET = 3, TECH_BIO = 2, TECH_ENGINEERING = 3) - materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 200) - build_path = /obj/item/device/robotanalyzer - sort_string = "MACFA" - -/datum/design/item/medical/mass_spectrometer - desc = "A device for analyzing chemicals in blood." - id = "mass_spectrometer" - req_tech = list(TECH_BIO = 2, TECH_MAGNET = 2) - build_path = /obj/item/device/mass_spectrometer - sort_string = "MACAA" - -/datum/design/item/medical/adv_mass_spectrometer - desc = "A device for analyzing chemicals in blood and their quantities." - id = "adv_mass_spectrometer" - req_tech = list(TECH_BIO = 2, TECH_MAGNET = 4) - build_path = /obj/item/device/mass_spectrometer/adv - sort_string = "MACAB" - -/datum/design/item/medical/reagent_scanner - desc = "A device for identifying chemicals." - id = "reagent_scanner" - req_tech = list(TECH_BIO = 2, TECH_MAGNET = 2) - build_path = /obj/item/device/reagent_scanner - sort_string = "MACBA" - -/datum/design/item/medical/adv_reagent_scanner - desc = "A device for identifying chemicals and their proportions." - id = "adv_reagent_scanner" - req_tech = list(TECH_BIO = 2, TECH_MAGNET = 4) - build_path = /obj/item/device/reagent_scanner/adv - sort_string = "MACBB" - -/datum/design/item/beaker/AssembleDesignName() - name = "Beaker prototype ([item_name])" - -/datum/design/item/beaker/noreact - name = "cryostasis" - desc = "A cryostasis beaker that allows for chemical storage without reactions. Can hold up to 50 units." - id = "splitbeaker" - req_tech = list(TECH_MATERIAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 3000) - build_path = /obj/item/weapon/reagent_containers/glass/beaker/noreact - sort_string = "MADAA" - -/datum/design/item/beaker/bluespace - name = TECH_BLUESPACE - desc = "A bluespace beaker, powered by experimental bluespace technology and Element Cuban combined with the Compound Pete. Can hold up to 300 units." - id = "bluespacebeaker" - req_tech = list(TECH_BLUESPACE = 2, TECH_MATERIAL = 6) - materials = list(DEFAULT_WALL_MATERIAL = 3000, "phoron" = 3000, "diamond" = 500) - build_path = /obj/item/weapon/reagent_containers/glass/beaker/bluespace - sort_string = "MADAB" - -/datum/design/item/medical/nanopaste - desc = "A tube of paste containing swarms of repair nanites. Very effective in repairing robotic machinery." - id = "nanopaste" - req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3) - materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 7000) - build_path = /obj/item/stack/nanopaste - sort_string = "MBAAA" +// Surgical devices /datum/design/item/medical/scalpel_laser1 name = "Basic Laser Scalpel" @@ -77,7 +14,7 @@ req_tech = list(TECH_BIO = 2, TECH_MATERIAL = 2, TECH_MAGNET = 2) materials = list(DEFAULT_WALL_MATERIAL = 12500, "glass" = 7500) build_path = /obj/item/weapon/surgical/scalpel/laser1 - sort_string = "MBBAA" + sort_string = "KAAAA" /datum/design/item/medical/scalpel_laser2 name = "Improved Laser Scalpel" @@ -86,7 +23,7 @@ req_tech = list(TECH_BIO = 3, TECH_MATERIAL = 4, TECH_MAGNET = 4) materials = list(DEFAULT_WALL_MATERIAL = 12500, "glass" = 7500, "silver" = 2500) build_path = /obj/item/weapon/surgical/scalpel/laser2 - sort_string = "MBBAB" + sort_string = "KAAAB" /datum/design/item/medical/scalpel_laser3 name = "Advanced Laser Scalpel" @@ -95,7 +32,7 @@ req_tech = list(TECH_BIO = 4, TECH_MATERIAL = 6, TECH_MAGNET = 5) materials = list(DEFAULT_WALL_MATERIAL = 12500, "glass" = 7500, "silver" = 2000, "gold" = 1500) build_path = /obj/item/weapon/surgical/scalpel/laser3 - sort_string = "MBBAC" + sort_string = "KAAAC" /datum/design/item/medical/scalpel_manager name = "Incision Management System" @@ -104,7 +41,25 @@ req_tech = list(TECH_BIO = 4, TECH_MATERIAL = 7, TECH_MAGNET = 5, TECH_DATA = 4) materials = list (DEFAULT_WALL_MATERIAL = 12500, "glass" = 7500, "silver" = 1500, "gold" = 1500, "diamond" = 750) build_path = /obj/item/weapon/surgical/scalpel/manager - sort_string = "MBBAD" + sort_string = "KAAAD" + +/datum/design/item/medical/saw_manager + name = "Energetic Bone Diverter" + desc = "A strange development following the I.M.S., this heavy tool can split and open, or close and shut, intentional holes in bones." + id = "advanced_saw" + req_tech = list(TECH_BIO = 4, TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_DATA = 5) + materials = list (DEFAULT_WALL_MATERIAL = 12500, MAT_PLASTIC = 800, "silver" = 1500, "gold" = 1500, MAT_OSMIUM = 1000) + build_path = /obj/item/weapon/surgical/circular_saw/manager + sort_string = "KAAAE" + +/datum/design/item/medical/organ_ripper + name = "Organ Ripper" + desc = "A modern and horrifying take on an ancient practice, this tool is capable of rapidly removing an organ from a hopefully willing patient, without damaging it." + id = "organ_ripper" + req_tech = list(TECH_BIO = 3, TECH_MATERIAL = 5, TECH_MAGNET = 4, TECH_ILLEGAL = 3) + materials = list (DEFAULT_WALL_MATERIAL = 12500, MAT_PLASTIC = 8000, MAT_OSMIUM = 2500) + build_path = /obj/item/weapon/surgical/scalpel/ripper + sort_string = "KAAAF" /datum/design/item/medical/bone_clamp name = "Bone Clamp" @@ -113,16 +68,16 @@ req_tech = list(TECH_BIO = 4, TECH_MATERIAL = 5, TECH_MAGNET = 4, TECH_DATA = 4) materials = list (DEFAULT_WALL_MATERIAL = 12500, "glass" = 7500, "silver" = 2500) build_path = /obj/item/weapon/surgical/bone_clamp - sort_string = "MBBAE" + sort_string = "KAABA" -/datum/design/item/medical/advanced_roller - name = "advanced roller bed" - desc = "A more advanced version of the regular roller bed, with inbuilt surgical stabilisers and an improved folding system." - id = "roller_bed" - req_tech = list(TECH_BIO = 3, TECH_MATERIAL = 3, TECH_MAGNET = 3) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "phoron" = 2000) - build_path = /obj/item/roller/adv - sort_string = "MBBAF" +/datum/design/item/medical/medical_analyzer + name = "health analyzer" + desc = "A hand-held body scanner able to distinguish vital signs of the subject." + id = "medical_analyzer" + req_tech = list(TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 500) + build_path = /obj/item/device/healthanalyzer + sort_string = "KBAAA" /datum/design/item/medical/improved_analyzer name = "improved health analyzer" @@ -131,56 +86,13 @@ req_tech = list(TECH_MAGNET = 5, TECH_BIO = 6) materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 1500) build_path = /obj/item/device/healthanalyzer/improved - sort_string = "MBBAG" + sort_string = "KBAAB" -/datum/design/item/implant - materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - -/datum/design/item/implant/AssembleDesignName() - ..() - name = "Implantable biocircuit design ([item_name])" - -/datum/design/item/implant/chemical - name = "chemical" - id = "implant_chem" - req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3) - build_path = /obj/item/weapon/implantcase/chem - sort_string = "MFAAA" - -/datum/design/item/implant/freedom - name = "freedom" - id = "implant_free" - req_tech = list(TECH_ILLEGAL = 2, TECH_BIO = 3) - build_path = /obj/item/weapon/implantcase/freedom - sort_string = "MFAAB" - -// These are in here because Robotics is close enough to Medical and I don't want to make a new brains.dm file -/datum/design/item/dronebrain - name = "Robotic intelligence circuit" - id = "dronebrain" - req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_DATA = 4) - build_type = PROTOLATHE | PROSFAB - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 500) - build_path = /obj/item/device/mmi/digital/robot - category = "Misc" - sort_string = "VACAC" - -/datum/design/item/posibrain - name = "Positronic brain" - id = "posibrain" - req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 6, TECH_BLUESPACE = 2, TECH_DATA = 4) - build_type = PROTOLATHE | PROSFAB - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 500, "phoron" = 500, "diamond" = 100) - build_path = /obj/item/device/mmi/digital/posibrain - category = "Misc" - sort_string = "VACAB" - -/datum/design/item/mmi - name = "Man-machine interface" - id = "mmi" - req_tech = list(TECH_DATA = 2, TECH_BIO = 3) - build_type = PROTOLATHE | PROSFAB - materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500) - build_path = /obj/item/device/mmi - category = "Misc" - sort_string = "VACBA" \ No newline at end of file +/datum/design/item/medical/advanced_roller + name = "advanced roller bed" + desc = "A more advanced version of the regular roller bed, with inbuilt surgical stabilisers and an improved folding system." + id = "roller_bed" + req_tech = list(TECH_BIO = 3, TECH_MATERIAL = 3, TECH_MAGNET = 3) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "phoron" = 2000) + build_path = /obj/item/roller/adv + sort_string = "KCAAA" \ No newline at end of file diff --git a/code/modules/research/designs/medical_vr.dm b/code/modules/research/designs/medical_vr.dm new file mode 100644 index 0000000000..7a0b0aa537 --- /dev/null +++ b/code/modules/research/designs/medical_vr.dm @@ -0,0 +1,244 @@ +/* + KV - ML3M stuff + KVA - gun + KVB - magazines + KVC - cells + KVCA - tier 0 + KVCB - tier 1 + KVCC - tier 2 + KVCD - tier 3 + KVCE - tier 4 + KVCO - tierless +*/ + +//General stuff + +/datum/design/item/medical/sleevemate + name = "SleeveMate 3700" + id = "sleevemate" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) + build_path = /obj/item/device/sleevemate + sort_string = "KCAVA" + +/datum/design/item/medical/protohypospray + name = "prototype hypospray" + desc = "This prototype hypospray is a sterile, air-needle autoinjector for rapid administration of drugs to patients." + id = "protohypospray" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_POWER = 2, TECH_BIO = 4, TECH_ILLEGAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 1500, "silver" = 2000, "gold" = 1500, "uranium" = 1000) + build_path = /obj/item/weapon/reagent_containers/hypospray/science + sort_string = "KCAVB" + +// ML-3M medigun and cells +/datum/design/item/medical/cell_based/AssembleDesignName() + ..() + name = "Cell-based medical prototype ([item_name])" + +/datum/design/item/medical/cell_based/cell_medigun + name = "cell-loaded medigun" + id = "cell_medigun" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 4, TECH_POWER = 3, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 8000, "plastic" = 8000, "glass" = 5000, "silver" = 1000, "gold" = 1000, "uranium" = 1000) + build_path = /obj/item/weapon/gun/projectile/cell_loaded/medical + sort_string = "KVAAA" + +/datum/design/item/medical/cell_based/cell_medigun_mag + name = "medical cell magazine" + id = "cell_medigun_mag" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 4, TECH_POWER = 3, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "plastic" = 6000, "glass" = 3000, "silver" = 500, "gold" = 500) + build_path = /obj/item/ammo_magazine/cell_mag/medical + sort_string = "KVBAA" + +/datum/design/item/medical/cell_based/cell_medigun_mag_advanced + name = "advanced medical cell magazine" + id = "cell_medigun_mag_advanced" + req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 4, TECH_BIO = 7) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "plastic" = 10000, "glass" = 5000, "silver" = 1500, "gold" = 1500, "diamond" = 5000) + build_path = /obj/item/ammo_magazine/cell_mag/medical/advanced + sort_string = "KVBAB" + +/datum/design/item/ml3m_cell/AssembleDesignName() + ..() + name = "Nanite cell prototype ([name])" + +//Tier 0 + +/datum/design/item/ml3m_cell/brute + name = "BRUTE" + id = "ml3m_cell_brute" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000) + build_path = /obj/item/ammo_casing/microbattery/medical/brute + sort_string = "KVCAA" + +/datum/design/item/ml3m_cell/burn + name = "BURN" + id = "ml3m_cell_burn" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000) + build_path = /obj/item/ammo_casing/microbattery/medical/burn + sort_string = "KVCAB" + +/datum/design/item/ml3m_cell/stabilize + name = "STABILIZE" + id = "ml3m_cell_stabilize" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000) + build_path = /obj/item/ammo_casing/microbattery/medical/stabilize + sort_string = "KVCAC" + +//Tier 1 + +/datum/design/item/ml3m_cell/toxin + name = "TOXIN" + id = "ml3m_cell_toxin" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_BIO = 4) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500) + build_path = /obj/item/ammo_casing/microbattery/medical/toxin + sort_string = "KVCBA" + +/datum/design/item/ml3m_cell/omni + name = "OMNI" + id = "ml3m_cell_omni" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_BIO = 4) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500) + build_path = /obj/item/ammo_casing/microbattery/medical/omni + sort_string = "KVCBB" + +/datum/design/item/ml3m_cell/antirad + name = "ANTIRAD" + id = "ml3m_cell_antirad" + req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_BIO = 4) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500) + build_path = /obj/item/ammo_casing/microbattery/medical/antirad + sort_string = "KVCBC" + +//Tier 2 + +/datum/design/item/ml3m_cell/brute2 + name = "BRUTE-II" + id = "ml3m_cell_brute2" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "gold" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/brute2 + sort_string = "KVCCA" + +/datum/design/item/ml3m_cell/burn2 + name = "BURN-II" + id = "ml3m_cell_burn2" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "gold" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/burn2 + sort_string = "KVCCB" + +/datum/design/item/ml3m_cell/stabilize2 + name = "STABILIZE-II" + id = "ml3m_cell_stabilize2" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "silver" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/stabilize2 + sort_string = "KVCCC" + +/datum/design/item/ml3m_cell/omni2 + name = "OMNI-II" + id = "ml3m_cell_omni2" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "uranium" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/omni2 + sort_string = "KVCCD" + +//Tier 3 + +/datum/design/item/ml3m_cell/toxin2 + name = "TOXIN-II" + id = "ml3m_cell_toxin2" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "uranium" = 1000, "silver" = 1000, "diamond" = 500) + build_path = /obj/item/ammo_casing/microbattery/medical/toxin2 + sort_string = "KVCDA" + +/datum/design/item/ml3m_cell/haste + name = "HASTE" + id = "ml3m_cell_haste" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "gold" = 1000, "silver" = 1000, "diamond" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/haste + sort_string = "KVCDB" + +/datum/design/item/ml3m_cell/resist + name = "RESIST" + id = "ml3m_cell_resist" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "gold" = 1000, "uranium" = 1000, "diamond" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/resist + sort_string = "KVCDC" + +/datum/design/item/ml3m_cell/corpse_mend + name = "CORPSE MEND" + id = "ml3m_cell_corpse_mend" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "phoron" = 3000, "diamond" = 3000) + build_path = /obj/item/ammo_casing/microbattery/medical/corpse_mend + sort_string = "KVCDD" + +//Tier 4 + +/datum/design/item/ml3m_cell/brute3 + name = "BRUTE-III" + id = "ml3m_cell_brute3" + req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_PRECURSOR = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "diamond" = 500, "verdantium" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/brute3 + sort_string = "KVCEA" + +/datum/design/item/ml3m_cell/burn3 + name = "BURN-III" + id = "ml3m_cell_burn3" + req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_PRECURSOR = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "diamond" = 500, "verdantium" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/burn3 + sort_string = "KVCEB" + +/datum/design/item/ml3m_cell/toxin3 + name = "TOXIN-III" + id = "ml3m_cell_toxin3" + req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_ARCANE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "diamond" = 500, "verdantium" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/toxin3 + sort_string = "KVCEC" + +/datum/design/item/ml3m_cell/omni3 + name = "OMNI-III" + id = "ml3m_cell_omni3" + req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_ARCANE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "diamond" = 500, "verdantium" = 1000) + build_path = /obj/item/ammo_casing/microbattery/medical/omni3 + sort_string = "KVCED" + +//Tierless + +/datum/design/item/ml3m_cell/shrink + name = "SHRINK" + id = "ml3m_cell_shrink" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_BLUESPACE = 3, TECH_BIO = 5, TECH_ILLEGAL = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "uranium" = 2000) + build_path = /obj/item/ammo_casing/microbattery/medical/shrink + sort_string = "KVCOA" + +/datum/design/item/ml3m_cell/grow + name = "GROW" + id = "ml3m_cell_grow" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_BLUESPACE = 3, TECH_BIO = 5, TECH_ILLEGAL = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "uranium" = 2000) + build_path = /obj/item/ammo_casing/microbattery/medical/grow + sort_string = "KVCOB" + +/datum/design/item/ml3m_cell/normalsize + name = "NORMALSIZE" + id = "ml3m_cell_normalsize" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_BLUESPACE = 3, TECH_BIO = 5, TECH_ILLEGAL = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "plastic" = 2500, "uranium" = 2000) + build_path = /obj/item/ammo_casing/microbattery/medical/normalsize + sort_string = "KVCOC" \ No newline at end of file diff --git a/code/modules/research/designs/mining_toys.dm b/code/modules/research/designs/mining_toys.dm index f9b76032cb..61be9cd871 100644 --- a/code/modules/research/designs/mining_toys.dm +++ b/code/modules/research/designs/mining_toys.dm @@ -1,48 +1,50 @@ -// Assorted Mining-related items - /datum/design/item/weapon/mining/AssembleDesignName() ..() name = "Mining equipment design ([item_name])" -/datum/design/item/weapon/mining/jackhammer - id = "jackhammer" - req_tech = list(TECH_MATERIAL = 3, TECH_POWER = 2, TECH_ENGINEERING = 2) - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 500, "silver" = 500) - build_path = /obj/item/weapon/pickaxe/jackhammer - sort_string = "KAAAA" +// Mining digging devices /datum/design/item/weapon/mining/drill id = "drill" req_tech = list(TECH_MATERIAL = 2, TECH_POWER = 3, TECH_ENGINEERING = 2) materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 1000) //expensive, but no need for miners. build_path = /obj/item/weapon/pickaxe/drill - sort_string = "KAAAB" + sort_string = "FAAAA" + +/datum/design/item/weapon/mining/jackhammer + id = "jackhammer" + req_tech = list(TECH_MATERIAL = 3, TECH_POWER = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 500, "silver" = 500) + build_path = /obj/item/weapon/pickaxe/jackhammer + sort_string = "FAAAB" /datum/design/item/weapon/mining/plasmacutter id = "plasmacutter" req_tech = list(TECH_MATERIAL = 4, TECH_PHORON = 3, TECH_ENGINEERING = 3) materials = list(DEFAULT_WALL_MATERIAL = 1500, "glass" = 500, "gold" = 500, "phoron" = 500) build_path = /obj/item/weapon/pickaxe/plasmacutter - sort_string = "KAAAC" + sort_string = "FAAAC" /datum/design/item/weapon/mining/pick_diamond id = "pick_diamond" req_tech = list(TECH_MATERIAL = 6) materials = list("diamond" = 3000) build_path = /obj/item/weapon/pickaxe/diamond - sort_string = "KAAAD" + sort_string = "FAAAD" /datum/design/item/weapon/mining/drill_diamond id = "drill_diamond" req_tech = list(TECH_MATERIAL = 6, TECH_POWER = 4, TECH_ENGINEERING = 4) materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 1000, "diamond" = 2000) build_path = /obj/item/weapon/pickaxe/diamonddrill - sort_string = "KAAAE" + sort_string = "FAAAE" -/datum/design/item/device/depth_scanner +// Mining other equipment + +/datum/design/item/weapon/mining/depth_scanner desc = "Used to check spatial depth and density of rock outcroppings." id = "depth_scanner" req_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2, TECH_BLUESPACE = 2) materials = list(DEFAULT_WALL_MATERIAL = 1000,"glass" = 1000) build_path = /obj/item/device/depth_scanner - sort_string = "KAAAF" \ No newline at end of file + sort_string = "FBAAA" diff --git a/code/modules/research/designs/misc.dm b/code/modules/research/designs/misc.dm index 29231a7cd9..8faf037136 100644 --- a/code/modules/research/designs/misc.dm +++ b/code/modules/research/designs/misc.dm @@ -1,203 +1,65 @@ -/* -// -// THIS IS GOING TO GET REAL DAMN BLOATED, SO LET'S TRY TO AVOID THAT IF POSSIBLE -// -*/ +// Everything that didn't fit elsewhere -/datum/design/item/hud - materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - -/datum/design/item/hud/AssembleDesignName() +/datum/design/item/general/AssembleDesignName() ..() - name = "HUD glasses prototype ([item_name])" + name = "General purpose design ([item_name])" -/datum/design/item/hud/AssembleDesignDesc() - desc = "Allows for the construction of \a [item_name] HUD glasses." +/datum/design/item/general/communicator + name = "Communicator" + id = "communicator" + req_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 500) + build_path = /obj/item/device/communicator + sort_string = "TAAAA" -/datum/design/item/hud/health - name = "health scanner" - id = "health_hud" - req_tech = list(TECH_BIO = 2, TECH_MAGNET = 3) - build_path = /obj/item/clothing/glasses/hud/health - sort_string = "GAAAA" - -/datum/design/item/hud/security - name = "security records" - id = "security_hud" - req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 2) - build_path = /obj/item/clothing/glasses/hud/security - sort_string = "GAAAB" - -/datum/design/item/hud/mesons - name = "Optical meson scanners design" - desc = "Using the meson-scanning technology those glasses allow you to see through walls, floor or anything else." - id = "mesons" - req_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) - materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - build_path = /obj/item/clothing/glasses/meson - sort_string = "GAAAC" - -/datum/design/item/device/ano_scanner - name = "Alden-Saraspova counter" - id = "ano_scanner" - desc = "Aids in triangulation of exotic particles." - req_tech = list(TECH_BLUESPACE = 3, TECH_MAGNET = 3) - materials = list(DEFAULT_WALL_MATERIAL = 10000,"glass" = 5000) - build_path = /obj/item/device/ano_scanner - sort_string = "UAAAH" - -/datum/design/item/light_replacer - name = "Light replacer" - desc = "A device to automatically replace lights. Refill with working lightbulbs." - id = "light_replacer" - req_tech = list(TECH_MAGNET = 3, TECH_MATERIAL = 4) - materials = list(DEFAULT_WALL_MATERIAL = 1500, "silver" = 150, "glass" = 3000) - build_path = /obj/item/device/lightreplacer - sort_string = "VAAAH" - -datum/design/item/laserpointer +datum/design/item/general/laserpointer name = "laser pointer" desc = "Don't shine it in your eyes!" id = "laser_pointer" req_tech = list(TECH_MAGNET = 3) materials = list(DEFAULT_WALL_MATERIAL = 100, "glass" = 50) build_path = /obj/item/device/laser_pointer - sort_string = "VAAAI" + sort_string = "TAABA" -/datum/design/item/paicard - name = "'pAI', personal artificial intelligence device" - id = "paicard" - req_tech = list(TECH_DATA = 2) - materials = list("glass" = 500, DEFAULT_WALL_MATERIAL = 500) - build_path = /obj/item/device/paicard - sort_string = "VABAI" - -/datum/design/item/communicator - name = "Communicator" - id = "communicator" - req_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) - materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 500) - build_path = /obj/item/device/communicator - sort_string = "VABAJ" - -/datum/design/item/beacon - name = "Bluespace tracking beacon design" - id = "beacon" - req_tech = list(TECH_BLUESPACE = 1) - materials = list (DEFAULT_WALL_MATERIAL = 20, "glass" = 10) - build_path = /obj/item/device/radio/beacon - sort_string = "VADAA" - -/datum/design/item/gps - name = "Triangulating device design" - desc = "Triangulates approximate co-ordinates using a nearby satellite network." - id = "gps" - req_tech = list(TECH_MATERIAL = 2, TECH_DATA = 2, TECH_BLUESPACE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 500) - build_path = /obj/item/device/gps - sort_string = "VADAB" - -/datum/design/item/beacon_locator - name = "Beacon tracking pinpointer" - desc = "Used to scan and locate signals on a particular frequency." - id = "beacon_locator" - req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 2, TECH_BLUESPACE = 3) - materials = list(DEFAULT_WALL_MATERIAL = 1000,"glass" = 500) - build_path = /obj/item/device/beacon_locator - sort_string = "VADAC" - -/datum/design/item/bag_holding - name = "'Bag of Holding', an infinite capacity bag prototype" - desc = "Using localized pockets of bluespace this bag prototype offers incredible storage capacity with the contents weighting nothing. It's a shame the bag itself is pretty heavy." - id = "bag_holding" - req_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 6) - materials = list("gold" = 3000, "diamond" = 1500, "uranium" = 250) - build_path = /obj/item/weapon/storage/backpack/holding - sort_string = "VAEAA" - -/datum/design/item/dufflebag_holding - name = "'DuffleBag of Holding', an infinite capacity dufflebag prototype" - desc = "A minaturized prototype of the popular Bag of Holding, the Dufflebag of Holding is, functionally, identical to the bag of holding, but comes in a more stylish and compact form." - id = "dufflebag_holding" - req_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 6) - materials = list("gold" = 3000, "diamond" = 1500, "uranium" = 250) - build_path = /obj/item/weapon/storage/backpack/holding/duffle - sort_string = "VAEAB" - -/datum/design/item/experimental_welder - name = "Experimental welding tool" - desc = "A welding tool that generate fuel for itself." - id = "expwelder" - req_tech = list(TECH_ENGINEERING = 4, TECH_PHORON = 3, TECH_MATERIAL = 4) - materials = list(DEFAULT_WALL_MATERIAL = 70, "glass" = 120, "phoron" = 100) - build_path = /obj/item/weapon/weldingtool/experimental - sort_string = "VASCA" - -/datum/design/item/hand_drill - name = "Hand drill" - desc = "A simple powered hand drill." - id = "handdrill" - req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 300, "silver" = 100) - build_path = /obj/item/weapon/tool/screwdriver/power - sort_string = "VASDA" - -/datum/design/item/jaws_life - name = "Jaws of life" - desc = "A set of jaws of life, compressed through the magic of science." - id = "jawslife" - req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 300, "silver" = 100) - build_path = /obj/item/weapon/tool/crowbar/power - sort_string = "VASEA" - -/datum/design/item/device/t_scanner_upg - name = "Upgraded T-ray Scanner" - desc = "An upgraded version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - id = "upgradedtscanner" - req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 4, TECH_MATERIAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 500, "phoron" = 150) - build_path = /obj/item/device/t_scanner/upgraded - sort_string = "VASSA" - -/datum/design/item/device/t_scanner_adv - name = "Advanced T-ray Scanner" - desc = "An advanced version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - id = "advancedtscanner" - req_tech = list(TECH_MAGNET = 6, TECH_ENGINEERING = 6, TECH_MATERIAL = 6) - materials = list(DEFAULT_WALL_MATERIAL = 1250, "phoron" = 500, "silver" = 50) - build_path = /obj/item/device/t_scanner/advanced - sort_string = "VASSB" - -/datum/design/item/translator +/datum/design/item/general/translator name = "handheld translator" id = "translator" req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 3000) build_path = /obj/item/device/universal_translator - sort_string = "HABQA" + sort_string = "TAACA" -/datum/design/item/ear_translator +/datum/design/item/general/ear_translator name = "earpiece translator" id = "ear_translator" req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5) //It's been hella miniaturized. materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 2000, "gold" = 1000) build_path = /obj/item/device/universal_translator/ear - sort_string = "HABQB" + sort_string = "TAACB" -/datum/design/item/xenoarch_multi_tool - name = "xenoarcheology multitool" - id = "xenoarch_multitool" - req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3, TECH_BLUESPACE = 3) - build_path = /obj/item/device/xenoarch_multi_tool - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "uranium" = 500, "phoron" = 500) - sort_string = "HABQC" +/datum/design/item/general/light_replacer + name = "Light replacer" + desc = "A device to automatically replace lights. Refill with working lightbulbs." + id = "light_replacer" + req_tech = list(TECH_MAGNET = 3, TECH_MATERIAL = 4) + materials = list(DEFAULT_WALL_MATERIAL = 1500, "silver" = 150, "glass" = 3000) + build_path = /obj/item/device/lightreplacer + sort_string = "TAADA" -/datum/design/item/excavationdrill - name = "Excavation Drill" - id = "excavationdrill" - req_tech = list(TECH_MATERIAL = 3, TECH_POWER = 2, TECH_ENGINEERING = 2, TECH_BLUESPACE = 3) - build_type = PROTOLATHE - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) - build_path = /obj/item/weapon/pickaxe/excavationdrill - sort_string = "HABQD" +/datum/design/item/general/binaryencrypt + name = "Binary encryption key" + desc = "Allows for deciphering the binary channel on-the-fly." + id = "binaryencrypt" + req_tech = list(TECH_ILLEGAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 300, "glass" = 300) + build_path = /obj/item/device/encryptionkey/binary + sort_string = "TBAAA" + +/datum/design/item/general/chameleon + name = "Holographic equipment kit" + desc = "A kit of dangerous, high-tech equipment with changeable looks." + id = "chameleon" + req_tech = list(TECH_ILLEGAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500) + build_path = /obj/item/weapon/storage/box/syndie_kit/chameleon + sort_string = "TBAAB" diff --git a/code/modules/research/designs/misc_vr.dm b/code/modules/research/designs/misc_vr.dm new file mode 100644 index 0000000000..2b397185c3 --- /dev/null +++ b/code/modules/research/designs/misc_vr.dm @@ -0,0 +1,23 @@ +/datum/design/item/general/bluespace_jumpsuit + name = "Bluespace jumpsuit" + id = "bsjumpsuit" + req_tech = list(TECH_BLUESPACE = 2, TECH_MATERIAL = 3, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) + build_path = /obj/item/clothing/under/bluespace + sort_string = "TAVAA" + +/datum/design/item/general/sizegun + name = "Size gun" + id = "sizegun" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 2000, "uranium" = 2000) + build_path = /obj/item/weapon/gun/energy/sizegun + sort_string = "TAVAB" + +/datum/design/item/general/bodysnatcher + name = "Body Snatcher" + id = "bodysnatcher" + req_tech = list(TECH_MAGNET = 3, TECH_BIO = 3, TECH_ILLEGAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) + build_path = /obj/item/device/bodysnatcher + sort_string = "TBVAA" \ No newline at end of file diff --git a/code/modules/research/designs/modular_computer.dm b/code/modules/research/designs/modular_computer.dm new file mode 100644 index 0000000000..e78a776c1c --- /dev/null +++ b/code/modules/research/designs/modular_computer.dm @@ -0,0 +1,219 @@ +// Modular computer components +/datum/design/item/modularcomponent/AssembleDesignName() + ..() + name = "Computer part design ([item_name])" + +// Hard drives + +/datum/design/item/modularcomponent/disk/normal + name = "basic hard drive" + id = "hdd_basic" + req_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 100) + build_path = /obj/item/weapon/computer_hardware/hard_drive/ + sort_string = "VAAAA" + +/datum/design/item/modularcomponent/disk/advanced + name = "advanced hard drive" + id = "hdd_advanced" + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 200) + build_path = /obj/item/weapon/computer_hardware/hard_drive/advanced + sort_string = "VAAAB" + +/datum/design/item/modularcomponent/disk/super + name = "super hard drive" + id = "hdd_super" + req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 8000, "glass" = 400) + build_path = /obj/item/weapon/computer_hardware/hard_drive/super + sort_string = "VAAAC" + +/datum/design/item/modularcomponent/disk/cluster + name = "cluster hard drive" + id = "hdd_cluster" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4) + materials = list(DEFAULT_WALL_MATERIAL = 16000, "glass" = 800) + build_path = /obj/item/weapon/computer_hardware/hard_drive/cluster + sort_string = "VAAAD" + +/datum/design/item/modularcomponent/disk/small + name = "small hard drive" + id = "hdd_small" + req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 200) + build_path = /obj/item/weapon/computer_hardware/hard_drive/small + sort_string = "VAAAE" + +/datum/design/item/modularcomponent/disk/micro + name = "micro hard drive" + id = "hdd_micro" + req_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 100) + build_path = /obj/item/weapon/computer_hardware/hard_drive/micro + sort_string = "VAAAF" + +// Network cards + +/datum/design/item/modularcomponent/netcard/basic + name = "basic network card" + id = "netcard_basic" + req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 1) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 100) + build_path = /obj/item/weapon/computer_hardware/network_card + sort_string = "VBAAA" + +/datum/design/item/modularcomponent/netcard/advanced + name = "advanced network card" + id = "netcard_advanced" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 200) + build_path = /obj/item/weapon/computer_hardware/network_card/advanced + sort_string = "VBAAB" + +/datum/design/item/modularcomponent/netcard/wired + name = "wired network card" + id = "netcard_wired" + req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 400) + build_path = /obj/item/weapon/computer_hardware/network_card/wired + sort_string = "VBAAC" + +// Batteries + +/datum/design/item/modularcomponent/battery/normal + name = "standard battery module" + id = "bat_normal" + req_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 1) + materials = list(DEFAULT_WALL_MATERIAL = 2000) + build_path = /obj/item/weapon/computer_hardware/battery_module + sort_string = "VCAAA" + +/datum/design/item/modularcomponent/battery/advanced + name = "advanced battery module" + id = "bat_advanced" + req_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000) + build_path = /obj/item/weapon/computer_hardware/battery_module/advanced + sort_string = "VCAAB" + +/datum/design/item/modularcomponent/battery/super + name = "super battery module" + id = "bat_super" + req_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 8000) + build_path = /obj/item/weapon/computer_hardware/battery_module/super + sort_string = "VCAAC" + +/datum/design/item/modularcomponent/battery/ultra + name = "ultra battery module" + id = "bat_ultra" + req_tech = list(TECH_POWER = 5, TECH_ENGINEERING = 4) + materials = list(DEFAULT_WALL_MATERIAL = 16000) + build_path = /obj/item/weapon/computer_hardware/battery_module/ultra + sort_string = "VCAAD" + +/datum/design/item/modularcomponent/battery/nano + name = "nano battery module" + id = "bat_nano" + req_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 1) + materials = list(DEFAULT_WALL_MATERIAL = 2000) + build_path = /obj/item/weapon/computer_hardware/battery_module/nano + sort_string = "VCAAE" + +/datum/design/item/modularcomponent/battery/micro + name = "micro battery module" + id = "bat_micro" + req_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000) + build_path = /obj/item/weapon/computer_hardware/battery_module/micro + sort_string = "VCAAF" + +// Processor unit + +/datum/design/item/modularcomponent/cpu/ + name = "computer processor unit" + id = "cpu_normal" + req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 8000) + build_path = /obj/item/weapon/computer_hardware/processor_unit + sort_string = "VDAAA" + +/datum/design/item/modularcomponent/cpu/small + name = "computer microprocessor unit" + id = "cpu_small" + req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000) + build_path = /obj/item/weapon/computer_hardware/processor_unit/small + sort_string = "VDAAB" + +/datum/design/item/modularcomponent/cpu/photonic + name = "computer photonic processor unit" + id = "pcpu_normal" + req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 4) + materials = list(DEFAULT_WALL_MATERIAL = 32000, glass = 8000) + build_path = /obj/item/weapon/computer_hardware/processor_unit/photonic + sort_string = "VDAAC" + +/datum/design/item/modularcomponent/cpu/photonic/small + name = "computer photonic microprocessor unit" + id = "pcpu_small" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3) + materials = list(DEFAULT_WALL_MATERIAL = 16000, glass = 4000) + build_path = /obj/item/weapon/computer_hardware/processor_unit/photonic/small + sort_string = "VDAAD" + +// Other parts + +/datum/design/item/modularcomponent/cardslot + name = "RFID card slot" + id = "cardslot" + req_tech = list(TECH_DATA = 2) + materials = list(DEFAULT_WALL_MATERIAL = 3000) + build_path = /obj/item/weapon/computer_hardware/card_slot + sort_string = "VEAAA" + +/datum/design/item/modularcomponent/nanoprinter + name = "nano printer" + id = "nanoprinter" + req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 3000) + build_path = /obj/item/weapon/computer_hardware/nano_printer + sort_string = "VEAAB" + +/datum/design/item/modularcomponent/teslalink + name = "tesla link" + id = "teslalink" + req_tech = list(TECH_DATA = 2, TECH_POWER = 3, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 10000) + build_path = /obj/item/weapon/computer_hardware/tesla_link + sort_string = "VEAAC" + +// Data crystals (USB flash drives) + +/datum/design/item/modularcomponent/portabledrive/AssembleDesignName() + ..() + name = "Portable data drive design ([item_name])" + +/datum/design/item/modularcomponent/portabledrive/basic + name = "basic data crystal" + id = "portadrive_basic" + req_tech = list(TECH_DATA = 1) + materials = list("glass" = 8000) + build_path = /obj/item/weapon/computer_hardware/hard_drive/portable + sort_string = "VFAAA" + +/datum/design/item/modularcomponent/portabledrive/advanced + name = "advanced data crystal" + id = "portadrive_advanced" + req_tech = list(TECH_DATA = 2) + materials = list("glass" = 16000) + build_path = /obj/item/weapon/computer_hardware/hard_drive/portable/advanced + sort_string = "VFAAB" + +/datum/design/item/modularcomponent/portabledrive/super + name = "super data crystal" + id = "portadrive_super" + req_tech = list(TECH_DATA = 4) + materials = list("glass" = 32000) + build_path = /obj/item/weapon/computer_hardware/hard_drive/portable/super + sort_string = "VFAAC" diff --git a/code/modules/research/designs/pdas.dm b/code/modules/research/designs/pdas.dm index 4aca3062b9..78fd01ec23 100644 --- a/code/modules/research/designs/pdas.dm +++ b/code/modules/research/designs/pdas.dm @@ -1,13 +1,16 @@ -/datum/design/item/pda - name = "PDA design" +// PDA + +/datum/design/item/general/pda + name = "PDA" desc = "Cheaper than whiny non-digital assistants." id = "pda" req_tech = list(TECH_ENGINEERING = 2, TECH_POWER = 3) materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) build_path = /obj/item/device/pda - sort_string = "VAAAA" + sort_string = "WAAAA" // Cartridges + /datum/design/item/pda_cartridge req_tech = list(TECH_ENGINEERING = 2, TECH_POWER = 3) materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) @@ -19,74 +22,79 @@ /datum/design/item/pda_cartridge/cart_basic id = "cart_basic" build_path = /obj/item/weapon/cartridge - sort_string = "VBAAA" + sort_string = "WBAAA" /datum/design/item/pda_cartridge/engineering id = "cart_engineering" build_path = /obj/item/weapon/cartridge/engineering - sort_string = "VBAAB" + sort_string = "WBAAB" /datum/design/item/pda_cartridge/atmos id = "cart_atmos" build_path = /obj/item/weapon/cartridge/atmos - sort_string = "VBAAC" + sort_string = "WBAAC" /datum/design/item/pda_cartridge/medical id = "cart_medical" build_path = /obj/item/weapon/cartridge/medical - sort_string = "VBAAD" + sort_string = "WBAAD" /datum/design/item/pda_cartridge/chemistry id = "cart_chemistry" build_path = /obj/item/weapon/cartridge/chemistry - sort_string = "VBAAE" + sort_string = "WBAAE" /datum/design/item/pda_cartridge/security id = "cart_security" build_path = /obj/item/weapon/cartridge/security - sort_string = "VBAAF" + sort_string = "WBAAF" /datum/design/item/pda_cartridge/janitor id = "cart_janitor" build_path = /obj/item/weapon/cartridge/janitor - sort_string = "VBAAG" + sort_string = "WBAAG" /datum/design/item/pda_cartridge/science id = "cart_science" build_path = /obj/item/weapon/cartridge/signal/science - sort_string = "VBAAH" + sort_string = "WBAAH" /datum/design/item/pda_cartridge/quartermaster id = "cart_quartermaster" build_path = /obj/item/weapon/cartridge/quartermaster - sort_string = "VBAAI" + sort_string = "WBAAI" + +/datum/design/item/pda_cartridge/head + id = "cart_head" + build_path = /obj/item/weapon/cartridge/head + sort_string = "WBAAJ" /datum/design/item/pda_cartridge/hop id = "cart_hop" build_path = /obj/item/weapon/cartridge/hop - sort_string = "VBAAJ" + sort_string = "WBAAK" /datum/design/item/pda_cartridge/hos id = "cart_hos" build_path = /obj/item/weapon/cartridge/hos - sort_string = "VBAAK" + sort_string = "WBAAL" /datum/design/item/pda_cartridge/ce id = "cart_ce" build_path = /obj/item/weapon/cartridge/ce - sort_string = "VBAAL" + sort_string = "WBAAM" /datum/design/item/pda_cartridge/cmo id = "cart_cmo" build_path = /obj/item/weapon/cartridge/cmo - sort_string = "VBAAM" + sort_string = "WBAAN" /datum/design/item/pda_cartridge/rd id = "cart_rd" build_path = /obj/item/weapon/cartridge/rd - sort_string = "VBAAN" + sort_string = "WBAAO" /datum/design/item/pda_cartridge/captain id = "cart_captain" build_path = /obj/item/weapon/cartridge/captain - sort_string = "VBAAO" \ No newline at end of file + sort_string = "WBAAP" \ No newline at end of file diff --git a/code/modules/research/designs/powercells.dm b/code/modules/research/designs/power_cells.dm similarity index 93% rename from code/modules/research/designs/powercells.dm rename to code/modules/research/designs/power_cells.dm index 1ae3a3c361..a387f7cc4c 100644 --- a/code/modules/research/designs/powercells.dm +++ b/code/modules/research/designs/power_cells.dm @@ -22,7 +22,7 @@ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 50) build_path = /obj/item/weapon/cell category = "Misc" - sort_string = "DAAAA" + sort_string = "BAAAA" /datum/design/item/powercell/high name = "high-capacity" @@ -32,7 +32,7 @@ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 60) build_path = /obj/item/weapon/cell/high category = "Misc" - sort_string = "DAAAB" + sort_string = "BAAAB" /datum/design/item/powercell/super name = "super-capacity" @@ -41,7 +41,7 @@ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 70) build_path = /obj/item/weapon/cell/super category = "Misc" - sort_string = "DAAAC" + sort_string = "BAAAC" /datum/design/item/powercell/hyper name = "hyper-capacity" @@ -50,7 +50,7 @@ materials = list(DEFAULT_WALL_MATERIAL = 400, "gold" = 150, "silver" = 150, "glass" = 70) build_path = /obj/item/weapon/cell/hyper category = "Misc" - sort_string = "DAAAD" + sort_string = "BAAAD" /datum/design/item/powercell/device name = "device" @@ -59,7 +59,7 @@ materials = list(DEFAULT_WALL_MATERIAL = 350, "glass" = 25) build_path = /obj/item/weapon/cell/device category = "Misc" - sort_string = "DAABA" + sort_string = "BAABA" /datum/design/item/powercell/weapon name = "weapon" @@ -68,4 +68,4 @@ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 50) build_path = /obj/item/weapon/cell/device/weapon category = "Misc" - sort_string = "DAABB" \ No newline at end of file + sort_string = "BAABB" \ No newline at end of file diff --git a/code/modules/research/designs/precursor.dm b/code/modules/research/designs/precursor.dm index 88575e5667..bb6e98353f 100644 --- a/code/modules/research/designs/precursor.dm +++ b/code/modules/research/designs/precursor.dm @@ -1,6 +1,26 @@ -/* - * Contains Precursor and Anomalous designs for the Protolathe. - */ +//Anomaly + +/datum/design/item/anomaly/AssembleDesignName() + ..() + name = "Anomalous prototype ([item_name])" + +/datum/design/item/anomaly/AssembleDesignDesc() + if(!desc) + if(build_path) + var/obj/item/I = build_path + desc = initial(I.desc) + ..() + +/datum/design/item/anomaly/camotrap + name = "Chameleon Trap" + desc = "A self-miraging mechanical trap, capable of producing short bursts of electric current when triggered." + id = "hunt_trap" + materials = list(MAT_DURASTEEL = 3000, MAT_METALHYDROGEN = 1000, MAT_PHORON = 2000) + req_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_MAGNET = 4, TECH_PHORON = 2, TECH_ARCANE = 2) + build_path = /obj/item/weapon/beartrap/hunting + sort_string = "ZAAAA" + +// Precursor /datum/design/item/precursor/AssembleDesignName() ..() @@ -20,7 +40,7 @@ req_tech = list(TECH_ENGINEERING = 6, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_GOLD = 250, MAT_URANIUM = 2500) build_path = /obj/item/weapon/tool/crowbar/hybrid - sort_string = "PATAC" + sort_string = "ZBAAA" /datum/design/item/precursor/wrench name = "Hybrid Wrench" @@ -29,7 +49,7 @@ req_tech = list(TECH_ENGINEERING = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_PRECURSOR = 1) materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_SILVER = 300, MAT_URANIUM = 2000) build_path = /obj/item/weapon/tool/wrench/hybrid - sort_string = "PATAW" + sort_string = "ZBAAB" /datum/design/item/precursor/screwdriver name = "Hybrid Screwdriver" @@ -38,7 +58,7 @@ req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_PRECURSOR = 1) materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_PLASTIC = 8000, MAT_DIAMOND = 2000) build_path = /obj/item/weapon/tool/screwdriver/hybrid - sort_string = "PATAS" + sort_string = "ZBAAC" /datum/design/item/precursor/wirecutters name = "Hybrid Wirecutters" @@ -47,7 +67,7 @@ req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_PHORON = 2, TECH_PRECURSOR = 1) materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_PLASTIC = 8000, MAT_PHORON = 2750, MAT_DIAMOND = 2000) build_path = /obj/item/weapon/tool/wirecutters/hybrid - sort_string = "PATBW" + sort_string = "ZBAAD" /datum/design/item/precursor/welder name = "Hybrid Welding Tool" @@ -56,15 +76,14 @@ req_tech = list(TECH_ENGINEERING = 6, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PHORON = 3, TECH_MAGNET = 5, TECH_PRECURSOR = 1) materials = list(MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_METALHYDROGEN = 4750, MAT_URANIUM = 6000) build_path = /obj/item/weapon/weldingtool/experimental/hybrid - sort_string = "PATCW" + sort_string = "ZBAAE" -/datum/design/item/anomaly/AssembleDesignName() - ..() - name = "Anomalous prototype ([item_name])" -/datum/design/item/anomaly/AssembleDesignDesc() - if(!desc) - if(build_path) - var/obj/item/I = build_path - desc = initial(I.desc) - ..() \ No newline at end of file +/datum/design/item/precursor/janusmodule + name = "Blackbox Circuit Datamass" + desc = "A design that seems to be in a constantly shifting superposition." + id = "janus_module" + materials = list(MAT_DURASTEEL = 3000, MAT_MORPHIUM = 2000, MAT_METALHYDROGEN = 6000, MAT_URANIUM = 6000, MAT_VERDANTIUM = 1500) + req_tech = list(TECH_MATERIAL = 7, TECH_BLUESPACE = 5, TECH_MAGNET = 6, TECH_PHORON = 3, TECH_ARCANE = 1, TECH_PRECURSOR = 2) + build_path = /obj/random/janusmodule + sort_string = "ZBBAA" diff --git a/code/modules/research/designs/sort_string_readme.dm b/code/modules/research/designs/sort_string_readme.dm new file mode 100644 index 0000000000..11521522b8 --- /dev/null +++ b/code/modules/research/designs/sort_string_readme.dm @@ -0,0 +1,91 @@ +/* + This is a guide to sort strings and categorization of designs. + Its really helpful and neat-looking when items are sorted properly in general R&D list and not just haphazardly. + + sort_string basically sorts items in alphabetic order, using sort_string itself as reference. + + + A - stock parts all always go first, and above everything else + AA - parts themselves + AAAA - matter bins + AAAB - micro manipulators + AAAC - capacitors + AAAD - scanners + AAAE - micro-lasers + AB - part replacer(s) + B - power cells + BAAA - regular power cells + BAAB - small power cells + C - Tech disks + D - GPS/beacons/locators/etc + DA - GPSs + DB - beacon/locator + DBAA - locator + DBAB - beacon + E - HUDs + F - Mining equipment + FA - drills + FB - scanners and such + G - Xenoarch equipment + H - Xenobiology equipment + HA - weapons + HB - other + I - Beakers + J - Biotech scanners and such + JAAA - mass spectrometers + JAAB - reagent scanners + JAAC - borg stuff + JAAD - plant stuff + K - Medical equipment + KA - surgery equipment + KAAA - scalpels/IMS + KAAB - bone clamp + KB - health analyzers + KC - misc + L - Implants + M - Weapons + MA - Ranged weapon + MAA - Energy ranged weapons + MAB - Ballistic ranged weapons + MABB - Ballistic ammo + MAC - Phase weapons + MAD - Other ranged weapons (darts/sprayer/fuelrod) + MADB - misc ammo + MB - Melee weapons + MC - grenade casings + N - Engineering equipment + NA - tools + NB - scanners + O, P - placeholders in case new category is needed + Q - Bags of Holding + R - Telecomms stock parts + S - AI-holders + SA - brain holders + SB - pAI + SC - intellicore + T - Misc stuff + TA - general + TB - illegal + U - Integrated circuits stuff + UA - printer + UB - upgrade disks + UC - tools + UD - holders + V - Modular computer parts + VA - hard drives + VB - network cards + VC - batteries + VD - cpus + VE - accessories without upgrades + VF - data crystals + W - PDA stuff + WA - PDA + WB - PDA cartridges + X, Y - more placeholders + Z - anomaly/precursor items + ZA - anomaly + ZB - precursor + ZBA - precursor tools + ZBB - precursor other + +*/ \ No newline at end of file diff --git a/code/modules/research/designs/stock_parts.dm b/code/modules/research/designs/stock_parts.dm index b928c6bb99..18e3c01616 100644 --- a/code/modules/research/designs/stock_parts.dm +++ b/code/modules/research/designs/stock_parts.dm @@ -14,231 +14,194 @@ if(!desc) desc = "A stock part used in the construction of various devices." -/datum/design/item/stock_part/basic_capacitor - id = "basic_capacitor" - req_tech = list(TECH_POWER = 1) - materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - build_path = /obj/item/weapon/stock_parts/capacitor - sort_string = "CAAAA" - -/datum/design/item/stock_part/adv_capacitor - id = "adv_capacitor" - req_tech = list(TECH_POWER = 3) - materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - build_path = /obj/item/weapon/stock_parts/capacitor/adv - sort_string = "CAAAB" - -/datum/design/item/stock_part/super_capacitor - id = "super_capacitor" - req_tech = list(TECH_POWER = 5, TECH_MATERIAL = 4) - materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50, "gold" = 20) - build_path = /obj/item/weapon/stock_parts/capacitor/super - sort_string = "CAAAC" - -/datum/design/item/stock_part/hyper_capacitor - id = "hyper_capacitor" - req_tech = list(TECH_POWER = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 1, TECH_ARCANE = 1) - materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_GLASS = 100, MAT_VERDANTIUM = 30, MAT_DURASTEEL = 25) - build_path = /obj/item/weapon/stock_parts/capacitor/hyper - sort_string = "CAAAD" - -/datum/design/item/stock_part/omni_capacitor - id = "omni_capacitor" - req_tech = list(TECH_POWER = 7, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) - materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_DIAMOND = 1000, MAT_GLASS = 1000, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100) - build_path = /obj/item/weapon/stock_parts/capacitor/omni - sort_string = "CAAAE" - -/datum/design/item/stock_part/micro_mani - id = "micro_mani" - req_tech = list(TECH_MATERIAL = 1, TECH_DATA = 1) - materials = list(DEFAULT_WALL_MATERIAL = 30) - build_path = /obj/item/weapon/stock_parts/manipulator - sort_string = "CAABA" - -/datum/design/item/stock_part/nano_mani - id = "nano_mani" - req_tech = list(TECH_MATERIAL = 3, TECH_DATA = 2) - materials = list(DEFAULT_WALL_MATERIAL = 30) - build_path = /obj/item/weapon/stock_parts/manipulator/nano - sort_string = "CAABB" - -/datum/design/item/stock_part/pico_mani - id = "pico_mani" - req_tech = list(TECH_MATERIAL = 5, TECH_DATA = 2) - materials = list(DEFAULT_WALL_MATERIAL = 30) - build_path = /obj/item/weapon/stock_parts/manipulator/pico - sort_string = "CAABC" - -/datum/design/item/stock_part/hyper_mani - id = "hyper_mani" - req_tech = list(TECH_MATERIAL = 6, TECH_DATA = 3, TECH_ARCANE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_VERDANTIUM = 50, MAT_DURASTEEL = 50) - build_path = /obj/item/weapon/stock_parts/manipulator/hyper - sort_string = "CAABD" - -/datum/design/item/stock_part/omni_mani - id = "omni_mani" - req_tech = list(TECH_MATERIAL = 7, TECH_DATA = 4, TECH_PRECURSOR = 2) - materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTEEL = 500, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100) - build_path = /obj/item/weapon/stock_parts/manipulator/omni - sort_string = "CAABE" +// Matter Bins /datum/design/item/stock_part/basic_matter_bin id = "basic_matter_bin" req_tech = list(TECH_MATERIAL = 1) materials = list(DEFAULT_WALL_MATERIAL = 80) build_path = /obj/item/weapon/stock_parts/matter_bin - sort_string = "CAACA" + sort_string = "AAAAA" /datum/design/item/stock_part/adv_matter_bin id = "adv_matter_bin" req_tech = list(TECH_MATERIAL = 3) materials = list(DEFAULT_WALL_MATERIAL = 80) build_path = /obj/item/weapon/stock_parts/matter_bin/adv - sort_string = "CAACB" + sort_string = "AAAAB" /datum/design/item/stock_part/super_matter_bin id = "super_matter_bin" req_tech = list(TECH_MATERIAL = 5) materials = list(DEFAULT_WALL_MATERIAL = 80) build_path = /obj/item/weapon/stock_parts/matter_bin/super - sort_string = "CAACC" + sort_string = "AAAAC" /datum/design/item/stock_part/hyper_matter_bin id = "hyper_matter_bin" req_tech = list(TECH_MATERIAL = 6, TECH_ARCANE = 2) materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_VERDANTIUM = 60, MAT_DURASTEEL = 75) build_path = /obj/item/weapon/stock_parts/matter_bin/hyper - sort_string = "CAACD" + sort_string = "AAAAD" /datum/design/item/stock_part/omni_matter_bin id = "omni_matter_bin" req_tech = list(TECH_MATERIAL = 7, TECH_PRECURSOR = 2) materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTEEL = 100, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100) build_path = /obj/item/weapon/stock_parts/matter_bin/omni - sort_string = "CAACE" + sort_string = "AAAAE" -/datum/design/item/stock_part/basic_micro_laser - id = "basic_micro_laser" - req_tech = list(TECH_MAGNET = 1) - materials = list(DEFAULT_WALL_MATERIAL = 10, "glass" = 20) - build_path = /obj/item/weapon/stock_parts/micro_laser - sort_string = "CAADA" +// Micro-manipulators -/datum/design/item/stock_part/high_micro_laser - id = "high_micro_laser" - req_tech = list(TECH_MAGNET = 3) - materials = list(DEFAULT_WALL_MATERIAL = 10, "glass" = 20) - build_path = /obj/item/weapon/stock_parts/micro_laser/high - sort_string = "CAADB" +/datum/design/item/stock_part/micro_mani + id = "micro_mani" + req_tech = list(TECH_MATERIAL = 1, TECH_DATA = 1) + materials = list(DEFAULT_WALL_MATERIAL = 30) + build_path = /obj/item/weapon/stock_parts/manipulator + sort_string = "AAABA" -/datum/design/item/stock_part/ultra_micro_laser - id = "ultra_micro_laser" - req_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 5) - materials = list(DEFAULT_WALL_MATERIAL = 10, "glass" = 20, "uranium" = 10) - build_path = /obj/item/weapon/stock_parts/micro_laser/ultra - sort_string = "CAADC" +/datum/design/item/stock_part/nano_mani + id = "nano_mani" + req_tech = list(TECH_MATERIAL = 3, TECH_DATA = 2) + materials = list(DEFAULT_WALL_MATERIAL = 30) + build_path = /obj/item/weapon/stock_parts/manipulator/nano + sort_string = "AAABB" -/datum/design/item/stock_part/hyper_micro_laser - id = "hyper_micro_laser" - req_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 6, TECH_ARCANE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_GLASS = 20, MAT_URANIUM = 30, MAT_VERDANTIUM = 50, MAT_DURASTEEL = 100) - build_path = /obj/item/weapon/stock_parts/micro_laser/hyper - sort_string = "CAADD" +/datum/design/item/stock_part/pico_mani + id = "pico_mani" + req_tech = list(TECH_MATERIAL = 5, TECH_DATA = 2) + materials = list(DEFAULT_WALL_MATERIAL = 30) + build_path = /obj/item/weapon/stock_parts/manipulator/pico + sort_string = "AAABC" -/datum/design/item/stock_part/omni_micro_laser - id = "omni_micro_laser" - req_tech = list(TECH_MAGNET = 7, TECH_MATERIAL = 7, TECH_PRECURSOR = 2) - materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_GLASS = 500, MAT_URANIUM = 2000, MAT_MORPHIUM = 50, MAT_DURASTEEL = 100) - build_path = /obj/item/weapon/stock_parts/micro_laser/omni - sort_string = "CAADE" +/datum/design/item/stock_part/hyper_mani + id = "hyper_mani" + req_tech = list(TECH_MATERIAL = 6, TECH_DATA = 3, TECH_ARCANE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_VERDANTIUM = 50, MAT_DURASTEEL = 50) + build_path = /obj/item/weapon/stock_parts/manipulator/hyper + sort_string = "AAABD" + +/datum/design/item/stock_part/omni_mani + id = "omni_mani" + req_tech = list(TECH_MATERIAL = 7, TECH_DATA = 4, TECH_PRECURSOR = 2) + materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTEEL = 500, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100) + build_path = /obj/item/weapon/stock_parts/manipulator/omni + sort_string = "AAABE" + +// Capacitors + +/datum/design/item/stock_part/basic_capacitor + id = "basic_capacitor" + req_tech = list(TECH_POWER = 1) + materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) + build_path = /obj/item/weapon/stock_parts/capacitor + sort_string = "AAACA" + +/datum/design/item/stock_part/adv_capacitor + id = "adv_capacitor" + req_tech = list(TECH_POWER = 3) + materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) + build_path = /obj/item/weapon/stock_parts/capacitor/adv + sort_string = "AAACB" + +/datum/design/item/stock_part/super_capacitor + id = "super_capacitor" + req_tech = list(TECH_POWER = 5, TECH_MATERIAL = 4) + materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50, "gold" = 20) + build_path = /obj/item/weapon/stock_parts/capacitor/super + sort_string = "AAACC" + +/datum/design/item/stock_part/hyper_capacitor + id = "hyper_capacitor" + req_tech = list(TECH_POWER = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 1, TECH_ARCANE = 1) + materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_GLASS = 100, MAT_VERDANTIUM = 30, MAT_DURASTEEL = 25) + build_path = /obj/item/weapon/stock_parts/capacitor/hyper + sort_string = "AAACD" + +/datum/design/item/stock_part/omni_capacitor + id = "omni_capacitor" + req_tech = list(TECH_POWER = 7, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) + materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_DIAMOND = 1000, MAT_GLASS = 1000, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100) + build_path = /obj/item/weapon/stock_parts/capacitor/omni + sort_string = "AAACE" + +// Sensors /datum/design/item/stock_part/basic_sensor id = "basic_sensor" req_tech = list(TECH_MAGNET = 1) materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 20) build_path = /obj/item/weapon/stock_parts/scanning_module - sort_string = "CAAEA" + sort_string = "AAADA" /datum/design/item/stock_part/adv_sensor id = "adv_sensor" req_tech = list(TECH_MAGNET = 3) materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 20) build_path = /obj/item/weapon/stock_parts/scanning_module/adv - sort_string = "CAAEB" + sort_string = "AAADB" /datum/design/item/stock_part/phasic_sensor id = "phasic_sensor" req_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 3) materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 20, "silver" = 10) build_path = /obj/item/weapon/stock_parts/scanning_module/phasic - sort_string = "CAAEC" + sort_string = "AAADC" /datum/design/item/stock_part/hyper_sensor id = "hyper_sensor" req_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 4, TECH_ARCANE = 1) materials = list(DEFAULT_WALL_MATERIAL = 50, MAT_GLASS = 20, MAT_SILVER = 50, MAT_VERDANTIUM = 40, MAT_DURASTEEL = 50) build_path = /obj/item/weapon/stock_parts/scanning_module/hyper - sort_string = "CAAED" + sort_string = "AAADD" /datum/design/item/stock_part/omni_sensor id = "omni_sensor" req_tech = list(TECH_MAGNET = 7, TECH_MATERIAL = 5, TECH_PRECURSOR = 1) materials = list(DEFAULT_WALL_MATERIAL = 1000, MAT_PLASTEEL = 500, MAT_GLASS = 750, MAT_SILVER = 500, MAT_MORPHIUM = 60, MAT_DURASTEEL = 100) build_path = /obj/item/weapon/stock_parts/scanning_module/omni - sort_string = "CAAEE" + sort_string = "AAADE" -/datum/design/item/stock_part/subspace_ansible - id = "s-ansible" - req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 80, "silver" = 20) - build_path = /obj/item/weapon/stock_parts/subspace/ansible - sort_string = "UAAAA" +// Micro-lasers -/datum/design/item/stock_part/hyperwave_filter - id = "s-filter" - req_tech = list(TECH_DATA = 3, TECH_MAGNET = 3) - materials = list(DEFAULT_WALL_MATERIAL = 40, "silver" = 10) - build_path = /obj/item/weapon/stock_parts/subspace/sub_filter - sort_string = "UAAAB" +/datum/design/item/stock_part/basic_micro_laser + id = "basic_micro_laser" + req_tech = list(TECH_MAGNET = 1) + materials = list(DEFAULT_WALL_MATERIAL = 10, "glass" = 20) + build_path = /obj/item/weapon/stock_parts/micro_laser + sort_string = "AAAEA" -/datum/design/item/stock_part/subspace_amplifier - id = "s-amplifier" - req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 10, "gold" = 30, "uranium" = 15) - build_path = /obj/item/weapon/stock_parts/subspace/amplifier - sort_string = "UAAAC" +/datum/design/item/stock_part/high_micro_laser + id = "high_micro_laser" + req_tech = list(TECH_MAGNET = 3) + materials = list(DEFAULT_WALL_MATERIAL = 10, "glass" = 20) + build_path = /obj/item/weapon/stock_parts/micro_laser/high + sort_string = "AAAEB" -/datum/design/item/stock_part/subspace_treatment - id = "s-treatment" - req_tech = list(TECH_DATA = 3, TECH_MAGNET = 2, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 10, "silver" = 20) - build_path = /obj/item/weapon/stock_parts/subspace/treatment - sort_string = "UAAAD" +/datum/design/item/stock_part/ultra_micro_laser + id = "ultra_micro_laser" + req_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 5) + materials = list(DEFAULT_WALL_MATERIAL = 10, "glass" = 20, "uranium" = 10) + build_path = /obj/item/weapon/stock_parts/micro_laser/ultra + sort_string = "AAAEC" -/datum/design/item/stock_part/subspace_analyzer - id = "s-analyzer" - req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 10, "gold" = 15) - build_path = /obj/item/weapon/stock_parts/subspace/analyzer - sort_string = "UAAAE" +/datum/design/item/stock_part/hyper_micro_laser + id = "hyper_micro_laser" + req_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 6, TECH_ARCANE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_GLASS = 20, MAT_URANIUM = 30, MAT_VERDANTIUM = 50, MAT_DURASTEEL = 100) + build_path = /obj/item/weapon/stock_parts/micro_laser/hyper + sort_string = "AAAED" -/datum/design/item/stock_part/subspace_crystal - id = "s-crystal" - req_tech = list(TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - materials = list("glass" = 1000, "silver" = 20, "gold" = 20) - build_path = /obj/item/weapon/stock_parts/subspace/crystal - sort_string = "UAAAF" +/datum/design/item/stock_part/omni_micro_laser + id = "omni_micro_laser" + req_tech = list(TECH_MAGNET = 7, TECH_MATERIAL = 7, TECH_PRECURSOR = 2) + materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_GLASS = 500, MAT_URANIUM = 2000, MAT_MORPHIUM = 50, MAT_DURASTEEL = 100) + build_path = /obj/item/weapon/stock_parts/micro_laser/omni + sort_string = "AAAEE" -/datum/design/item/stock_part/subspace_transmitter - id = "s-transmitter" - req_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 5, TECH_BLUESPACE = 3) - materials = list("glass" = 100, "silver" = 10, "uranium" = 15) - build_path = /obj/item/weapon/stock_parts/subspace/transmitter - sort_string = "UAAAG" -// RPEDs live here because they handle stock parts +// RPEDs + /datum/design/item/stock_part/RPED name = "Rapid Part Exchange Device" desc = "Special mechanical module made to store, sort, and apply standard machine parts." @@ -246,7 +209,7 @@ req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 3) materials = list(DEFAULT_WALL_MATERIAL = 15000, "glass" = 5000) build_path = /obj/item/weapon/storage/part_replacer - sort_string = "CBAAA" + sort_string = "ABAAA" /datum/design/item/stock_part/ARPED name = "Advanced Rapid Part Exchange Device" @@ -255,4 +218,4 @@ req_tech = list(TECH_ENGINEERING = 5, TECH_MATERIAL = 5) materials = list(DEFAULT_WALL_MATERIAL = 30000, "glass" = 10000) build_path = /obj/item/weapon/storage/part_replacer/adv - sort_string = "CBAAB" \ No newline at end of file + sort_string = "ABAAB" \ No newline at end of file diff --git a/code/modules/research/designs/subspace_parts.dm b/code/modules/research/designs/subspace_parts.dm new file mode 100644 index 0000000000..0fcc0e20e0 --- /dev/null +++ b/code/modules/research/designs/subspace_parts.dm @@ -0,0 +1,54 @@ +// Telecomm parts + +/datum/design/item/stock_part/subspace/AssembleDesignName() + ..() + name = "Subspace component design ([item_name])" + +/datum/design/item/stock_part/subspace/subspace_ansible + id = "s-ansible" + req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 80, "silver" = 20) + build_path = /obj/item/weapon/stock_parts/subspace/ansible + sort_string = "RAAAA" + +/datum/design/item/stock_part/subspace/hyperwave_filter + id = "s-filter" + req_tech = list(TECH_DATA = 3, TECH_MAGNET = 3) + materials = list(DEFAULT_WALL_MATERIAL = 40, "silver" = 10) + build_path = /obj/item/weapon/stock_parts/subspace/sub_filter + sort_string = "RAAAB" + +/datum/design/item/stock_part/subspace/subspace_amplifier + id = "s-amplifier" + req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 10, "gold" = 30, "uranium" = 15) + build_path = /obj/item/weapon/stock_parts/subspace/amplifier + sort_string = "RAAAC" + +/datum/design/item/stock_part/subspace/subspace_treatment + id = "s-treatment" + req_tech = list(TECH_DATA = 3, TECH_MAGNET = 2, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 10, "silver" = 20) + build_path = /obj/item/weapon/stock_parts/subspace/treatment + sort_string = "RAAAD" + +/datum/design/item/stock_part/subspace/subspace_analyzer + id = "s-analyzer" + req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 10, "gold" = 15) + build_path = /obj/item/weapon/stock_parts/subspace/analyzer + sort_string = "RAAAE" + +/datum/design/item/stock_part/subspace/subspace_crystal + id = "s-crystal" + req_tech = list(TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + materials = list("glass" = 1000, "silver" = 20, "gold" = 20) + build_path = /obj/item/weapon/stock_parts/subspace/crystal + sort_string = "RAAAF" + +/datum/design/item/stock_part/subspace/subspace_transmitter + id = "s-transmitter" + req_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 5, TECH_BLUESPACE = 3) + materials = list("glass" = 100, "silver" = 10, "uranium" = 15) + build_path = /obj/item/weapon/stock_parts/subspace/transmitter + sort_string = "RAAAG" \ No newline at end of file diff --git a/code/modules/research/designs/tech_disks.dm b/code/modules/research/designs/tech_disks.dm new file mode 100644 index 0000000000..70f6ed0022 --- /dev/null +++ b/code/modules/research/designs/tech_disks.dm @@ -0,0 +1,21 @@ +/datum/design/item/disk/AssembleDesignName() + ..() + name = "Data storage design ([name])" + +/datum/design/item/disk/design_disk + name = "Design Storage Disk" + desc = "Produce additional disks for storing device designs." + id = "design_disk" + req_tech = list(TECH_DATA = 1) + materials = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 10) + build_path = /obj/item/weapon/disk/design_disk + sort_string = "CAAAA" + +/datum/design/item/disk/tech_disk + name = "Technology Data Storage Disk" + desc = "Produce additional disks for storing technology data." + id = "tech_disk" + req_tech = list(TECH_DATA = 1) + materials = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 10) + build_path = /obj/item/weapon/disk/tech_disk + sort_string = "CAAAB" \ No newline at end of file diff --git a/code/modules/research/designs/weapons.dm b/code/modules/research/designs/weapons.dm index d696b14b29..81a2bd57d9 100644 --- a/code/modules/research/designs/weapons.dm +++ b/code/modules/research/designs/weapons.dm @@ -2,6 +2,10 @@ ..() name = "Weapon prototype ([item_name])" +/datum/design/item/weapon/ammo/AssembleDesignName() + ..() + name = "Weapon ammo prototype ([item_name])" + /datum/design/item/weapon/AssembleDesignDesc() if(!desc) if(build_path) @@ -9,95 +13,145 @@ desc = initial(I.desc) ..() -/datum/design/item/weapon/stunrevolver +// Energy weapons + +/datum/design/item/weapon/energy/AssembleDesignName() + ..() + name = "Energy weapon prototype ([item_name])" + +/datum/design/item/weapon/energy/stunrevolver id = "stunrevolver" req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_POWER = 2) materials = list(DEFAULT_WALL_MATERIAL = 4000) build_path = /obj/item/weapon/gun/energy/stunrevolver - sort_string = "TAAAA" + sort_string = "MAAAA" -/datum/design/item/weapon/nuclear_gun +/datum/design/item/weapon/energy/nuclear_gun id = "nuclear_gun" req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 5, TECH_POWER = 3) materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000, "uranium" = 500) build_path = /obj/item/weapon/gun/energy/gun/nuclear - sort_string = "TAAAB" + sort_string = "MAAAB" -/datum/design/item/weapon/lasercannon +/datum/design/item/weapon/energy/phoronpistol + id = "ppistol" + req_tech = list(TECH_COMBAT = 5, TECH_PHORON = 4) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000, "phoron" = 3000) + build_path = /obj/item/weapon/gun/energy/toxgun + sort_string = "MAAAC" + +/datum/design/item/weapon/energy/lasercannon desc = "The lasing medium of this prototype is enclosed in a tube lined with uranium-235 and subjected to high neutron flux in a nuclear reactor core." id = "lasercannon" req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 1000, "diamond" = 2000) build_path = /obj/item/weapon/gun/energy/lasercannon - sort_string = "TAAAC" + sort_string = "MAAAD" -/datum/design/item/weapon/phoronpistol - id = "ppistol" - req_tech = list(TECH_COMBAT = 5, TECH_PHORON = 4) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000, "phoron" = 3000) - build_path = /obj/item/weapon/gun/energy/toxgun - sort_string = "TAAAD" - -/datum/design/item/weapon/decloner +/datum/design/item/weapon/energy/decloner id = "decloner" req_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 7, TECH_BIO = 5, TECH_POWER = 6) materials = list("gold" = 5000,"uranium" = 10000) build_path = /obj/item/weapon/gun/energy/decloner - sort_string = "TAAAE" + sort_string = "MAAAE" -/datum/design/item/weapon/advanced_smg +/datum/design/item/weapon/energy/temp_gun + desc = "A gun that shoots high-powered glass-encased energy temperature bullets." + id = "temp_gun" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 4, TECH_POWER = 3, TECH_MAGNET = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 500, "silver" = 3000) + build_path = /obj/item/weapon/gun/energy/temperature + sort_string = "MAAAF" + +/datum/design/item/weapon/energy/flora_gun + id = "flora_gun" + req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3, TECH_POWER = 3) + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 500, "uranium" = 500) + build_path = /obj/item/weapon/gun/energy/floragun + sort_string = "MAAAG" + +// Ballistic weapons + +/datum/design/item/weapon/ballistic/AssembleDesignName() + ..() + name = "Ballistic weapon prototype ([item_name])" + +/datum/design/item/weapon/ballistic/advanced_smg id = "smg" desc = "An advanced 9mm SMG with a reflective laser optic." req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3) materials = list(DEFAULT_WALL_MATERIAL = 8000, "silver" = 2000, "diamond" = 1000) build_path = /obj/item/weapon/gun/projectile/automatic/advanced_smg - sort_string = "TAABA" + sort_string = "MABAA" -/datum/design/item/weapon/ammo_9mmAdvanced +// Ballistic ammo + +/datum/design/item/weapon/ballistic/ammo/AssembleDesignName() + ..() + name = "Ballistic weapon ammo prototype ([name])" + +/datum/design/item/weapon/ballistic/ammo/ammo_9mmAdvanced + name = "9mm magazine" id = "ammo_9mm" desc = "A 21 round magazine for an advanced 9mm SMG." req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3) materials = list(DEFAULT_WALL_MATERIAL = 3750, "silver" = 100) // Requires silver for proprietary magazines! Or something. build_path = /obj/item/ammo_magazine/m9mmAdvanced - sort_string = "TAACA" + sort_string = "MABBA" -/datum/design/item/weapon/stunshell +/datum/design/item/weapon/ballistic/ammo/stunshell + name = "stun shell" desc = "A stunning shell for a shotgun." id = "stunshell" req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3) materials = list(DEFAULT_WALL_MATERIAL = 4000) build_path = /obj/item/ammo_casing/a12g/stunshell - sort_string = "TAACB" + sort_string = "MABBB" -/datum/design/item/weapon/chemsprayer - desc = "An advanced chem spraying device." - id = "chemsprayer" - req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_BIO = 2) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000) - build_path = /obj/item/weapon/reagent_containers/spray/chemsprayer - sort_string = "TABAA" +// Phase weapons + +/datum/design/item/weapon/phase/AssembleDesignName() + ..() + name = "Phase weapon prototype ([item_name])" + +/* //VOREStation Removal Start +/datum/design/item/weapon/phase/phase_pistol + id = "phasepistol" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000) + build_path = /obj/item/weapon/gun/energy/phasegun/pistol + sort_string = "MACAA" + +/datum/design/item/weapon/phase/phase_carbine + id = "phasecarbine" + req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 1500) + build_path = /obj/item/weapon/gun/energy/phasegun + sort_string = "MACAB" + +/datum/design/item/weapon/phase/phase_rifle + id = "phaserifle" + req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) + materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 2000, "silver" = 500) + build_path = /obj/item/weapon/gun/energy/phasegun/rifle + sort_string = "MACAC" + +/datum/design/item/weapon/phase/phase_cannon + id = "phasecannon" + req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 4, TECH_POWER = 4) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 2000, "silver" = 1000, "diamond" = 750) + build_path = /obj/item/weapon/gun/energy/phasegun/cannon + sort_string = "MACAD" +*/ //VOREStation Removal End + +// Other weapons /datum/design/item/weapon/rapidsyringe id = "rapidsyringe" req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_BIO = 2) materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000) build_path = /obj/item/weapon/gun/launcher/syringe/rapid - sort_string = "TABAB" - -/datum/design/item/weapon/temp_gun - desc = "A gun that shoots high-powered glass-encased energy temperature bullets." - id = "temp_gun" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 4, TECH_POWER = 3, TECH_MAGNET = 2) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 500, "silver" = 3000) - build_path = /obj/item/weapon/gun/energy/temperature - sort_string = "TABAC" - -/datum/design/item/weapon/large_grenade - id = "large_Grenade" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 3000) - build_path = /obj/item/weapon/grenade/chem_grenade/large - sort_string = "TACAA" + sort_string = "MADAA" /datum/design/item/weapon/dartgun desc = "A gun that fires small hollow chemical-payload darts." @@ -105,92 +159,83 @@ req_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_BIO = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 1) materials = list(DEFAULT_WALL_MATERIAL = 5000, "gold" = 5000, "silver" = 2500, "glass" = 750) build_path = /obj/item/weapon/gun/projectile/dartgun/research - sort_string = "TACAB" + sort_string = "MADAB" -/datum/design/item/weapon/dartgunmag_small - id = "dartgun_mag_s" - req_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) - materials = list(DEFAULT_WALL_MATERIAL = 300, "gold" = 100, "silver" = 100, "glass" = 300) - build_path = /obj/item/ammo_magazine/chemdart/small - sort_string = "TACAC" - -/datum/design/item/weapon/dartgun_ammo_small - id = "dartgun_ammo_s" - req_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) - materials = list(DEFAULT_WALL_MATERIAL = 50, "gold" = 30, "silver" = 30, "glass" = 50) - build_path = /obj/item/ammo_casing/chemdart/small - sort_string = "TACAD" - -/datum/design/item/weapon/dartgunmag_med - id = "dartgun_mag_m" - req_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) - materials = list(DEFAULT_WALL_MATERIAL = 500, "gold" = 150, "silver" = 150, "diamond" = 200, "glass" = 400) - build_path = /obj/item/ammo_magazine/chemdart - sort_string = "TACAE" - -/datum/design/item/weapon/dartgun_ammo_med - id = "dartgun_ammo_m" - req_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) - materials = list(DEFAULT_WALL_MATERIAL = 80, "gold" = 40, "silver" = 40, "glass" = 60) - build_path = /obj/item/ammo_casing/chemdart - sort_string = "TACAF" +/datum/design/item/weapon/chemsprayer + desc = "An advanced chem spraying device." + id = "chemsprayer" + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000) + build_path = /obj/item/weapon/reagent_containers/spray/chemsprayer + sort_string = "MADAC" /datum/design/item/weapon/fuelrod id = "fuelrod_gun" req_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_PHORON = 4, TECH_ILLEGAL = 5, TECH_MAGNET = 5) materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 2000, "gold" = 500, "silver" = 500, "uranium" = 1000, "phoron" = 3000, "diamond" = 1000) build_path = /obj/item/weapon/gun/magnetic/fuelrod - sort_string = "TACBA" + sort_string = "MADAD" -/datum/design/item/weapon/flora_gun - id = "flora_gun" - req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3, TECH_POWER = 3) - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 500, "uranium" = 500) - build_path = /obj/item/weapon/gun/energy/floragun - sort_string = "TBAAA" +// Ammo for those -// Xenobio Tools -/datum/design/item/weapon/slimebation - id = "slimebation" - req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2, TECH_POWER = 3, TECH_COMBAT = 3) - materials = list(DEFAULT_WALL_MATERIAL = 5000) - build_path = /obj/item/weapon/melee/baton/slime - sort_string = "TBAAB" +/datum/design/item/weapon/ammo/dartgunmag_small + id = "dartgun_mag_s" + req_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) + materials = list(DEFAULT_WALL_MATERIAL = 300, "gold" = 100, "silver" = 100, "glass" = 300) + build_path = /obj/item/ammo_magazine/chemdart/small + sort_string = "MADBA" -/datum/design/item/weapon/slimetaser - id = "slimetaser" - req_tech = list(TECH_MATERIAL = 3, TECH_BIO = 3, TECH_POWER = 4, TECH_COMBAT = 4) - materials = list(DEFAULT_WALL_MATERIAL = 5000) - build_path = /obj/item/weapon/gun/energy/taser/xeno - sort_string = "TBAAC" +/datum/design/item/weapon/ammo/dartgun_ammo_small + id = "dartgun_ammo_s" + req_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) + materials = list(DEFAULT_WALL_MATERIAL = 50, "gold" = 30, "silver" = 30, "glass" = 50) + build_path = /obj/item/ammo_casing/chemdart/small + sort_string = "MADBB" -/* //VOREStation Edit -// Phase Weapons -/datum/design/item/weapon/phase_pistol - id = "phasepistol" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 4000) - build_path = /obj/item/weapon/gun/energy/phasegun/pistol - sort_string = "TPAAA" +/datum/design/item/weapon/ammo/dartgunmag_med + id = "dartgun_mag_m" + req_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) + materials = list(DEFAULT_WALL_MATERIAL = 500, "gold" = 150, "silver" = 150, "diamond" = 200, "glass" = 400) + build_path = /obj/item/ammo_magazine/chemdart + sort_string = "MADBC" -/datum/design/item/weapon/phase_carbine - id = "phasecarbine" - req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 2, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 1500) - build_path = /obj/item/weapon/gun/energy/phasegun - sort_string = "TPAAB" +/datum/design/item/weapon/ammo/dartgun_ammo_med + id = "dartgun_ammo_m" + req_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_MAGNET = 1, TECH_ILLEGAL = 1) + materials = list(DEFAULT_WALL_MATERIAL = 80, "gold" = 40, "silver" = 40, "glass" = 60) + build_path = /obj/item/ammo_casing/chemdart + sort_string = "MADBD" -/datum/design/item/weapon/phase_rifle - id = "phaserifle" - req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) - materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 2000, "silver" = 500) - build_path = /obj/item/weapon/gun/energy/phasegun/rifle - sort_string = "TPAAC" +// Melee weapons -/datum/design/item/weapon/phase_cannon - id = "phasecannon" - req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 4, TECH_POWER = 4) - materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 2000, "silver" = 1000, "diamond" = 750) - build_path = /obj/item/weapon/gun/energy/phasegun/cannon - sort_string = "TPAAD" -*/ \ No newline at end of file +/datum/design/item/weapon/melee/AssembleDesignName() + ..() + name = "Melee weapon prototype ([item_name])" + +/datum/design/item/weapon/melee/esword + name = "Portable Energy Blade" + id = "chargesword" + req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 4, TECH_ENGINEERING = 5, TECH_ILLEGAL = 4, TECH_ARCANE = 1) + materials = list(MAT_PLASTEEL = 3500, "glass" = 1000, MAT_LEAD = 2250, MAT_METALHYDROGEN = 500) + build_path = /obj/item/weapon/melee/energy/sword/charge + sort_string = "MBAAA" + +/datum/design/item/weapon/melee/eaxe + name = "Energy Axe" + id = "chargeaxe" + req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 5, TECH_ENGINEERING = 4, TECH_ILLEGAL = 4) + materials = list(MAT_PLASTEEL = 3500, MAT_OSMIUM = 2000, MAT_LEAD = 2000, MAT_METALHYDROGEN = 500) + build_path = /obj/item/weapon/melee/energy/axe/charge + sort_string = "MBAAB" + +// Grenade stuff +/datum/design/item/weapon/grenade/AssembleDesignName() + ..() + name = "Grenade casing prototype ([item_name])" + +/datum/design/item/weapon/grenade/large_grenade + id = "large_Grenade" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2) + materials = list(DEFAULT_WALL_MATERIAL = 3000) + build_path = /obj/item/weapon/grenade/chem_grenade/large + sort_string = "MCAAA" diff --git a/code/modules/research/designs/weapons_vr.dm b/code/modules/research/designs/weapons_vr.dm new file mode 100644 index 0000000000..0cde7758c4 --- /dev/null +++ b/code/modules/research/designs/weapons_vr.dm @@ -0,0 +1,165 @@ +/* + MAU - AP weapons + MAV - cell-loaded weapons + MAVA - weapon + MAVB - cartridge + MAVC - cells +*/ + + +// Energy Weapons + +/datum/design/item/weapon/energy/protector + desc = "The 'Protector' is an advanced energy gun that cannot be fired in lethal mode on low security alert levels, but features DNA locking and a powerful stun." + id = "protector" + req_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "silver" = 1000) + build_path = /obj/item/weapon/gun/energy/protector + sort_string = "MAAVA" + +/datum/design/item/weapon/energy/sickshot + desc = "A 'Sickshot' is a 4-shot energy revolver that causes nausea and confusion." + id = "sickshot" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_MAGNET = 2) + materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 2000) + build_path = /obj/item/weapon/gun/energy/sickshot + sort_string = "MAAVB" + +/datum/design/item/weapon/energy/netgun + name = "\'Hunter\' capture gun" + id = "netgun" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 5, TECH_MAGNET = 3) + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 3000) + build_path = /obj/item/weapon/gun/energy/netgun + sort_string = "MAAVC" + +// Misc weapons + +/datum/design/item/weapon/pummeler + desc = "With the 'Pummeler', punt anyone you don't like out of the room!" + id = "pummeler" + req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_MAGNET = 5) + materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 3000, "uranium" = 1000) + build_path = /obj/item/weapon/gun/energy/pummeler + sort_string = "MADVA" + +// Anti-particle stuff + +/datum/design/item/weapon/particle/AssembleDesignName() + ..() + name = "Anti-particle weapon prototype ([item_name])" + +/datum/design/item/weapon/particle/advparticle + name = "Advanced anti-particle rifle" + id = "advparticle" + req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 5, TECH_POWER = 3, TECH_MAGNET = 3) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000, "gold" = 1000, "uranium" = 750) + build_path = /obj/item/weapon/gun/energy/particle/advanced + sort_string = "MAAUA" + +/datum/design/item/weapon/particle/particlecannon + name = "Anti-particle cannon" + id = "particlecannon" + req_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 5, TECH_POWER = 4, TECH_MAGNET = 4) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 1500, "gold" = 2000, "uranium" = 1000, "diamond" = 2000) + build_path = /obj/item/weapon/gun/energy/particle/cannon + sort_string = "MAAUB" + +/datum/design/item/weapon/particle/pressureinterlock + name = "APP pressure interlock" + id = "pressureinterlock" + req_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 250) + build_path = /obj/item/pressurelock + sort_string = "MAAUC" + +// NSFW gun and cells +/datum/design/item/weapon/cell_based/AssembleDesignName() + ..() + name = "Cell-based weapon prototype ([item_name])" + +/datum/design/item/weapon/cell_based/prototype_nsfw + name = "cell-loaded revolver" + id = "nsfw_prototype" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 4, TECH_POWER = 4, TECH_COMBAT = 7) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 6000, "phoron" = 8000, "uranium" = 4000) + build_path = /obj/item/weapon/gun/projectile/cell_loaded/combat/prototype + sort_string = "MAVAA" + +/datum/design/item/weapon/cell_based/prototype_nsfw_mag + name = "combat cell magazine" + id = "nsfw_mag_prototype" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 4, TECH_POWER = 4, TECH_COMBAT = 7) + materials = list(DEFAULT_WALL_MATERIAL = 8000, "glass" = 4000, "phoron" = 4000) + build_path = /obj/item/ammo_magazine/cell_mag/combat/prototype + sort_string = "MAVBA" + +/datum/design/item/nsfw_cell/AssembleDesignName() + ..() + name = "Microbattery prototype ([name])" + +/datum/design/item/nsfw_cell/stun + name = "STUN" + id = "nsfw_cell_stun" + req_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 2, TECH_POWER = 3, TECH_COMBAT = 3) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000) + build_path = /obj/item/ammo_casing/microbattery/combat/stun + sort_string = "MAVCA" + +/datum/design/item/nsfw_cell/lethal + name = "LETHAL" + id = "nsfw_cell_lethal" + req_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_POWER = 3, TECH_COMBAT = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "phoron" = 3000) + build_path = /obj/item/ammo_casing/microbattery/combat/lethal + sort_string = "MAVCB" + +/datum/design/item/nsfw_cell/net + name = "NET" + id = "nsfw_cell_net" + req_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_POWER = 3, TECH_COMBAT = 4) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "uranium" = 3000) + build_path = /obj/item/ammo_casing/microbattery/combat/net + sort_string = "MAVCC" + +/datum/design/item/nsfw_cell/ion + name = "ION" + id = "nsfw_cell_ion" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 5, TECH_COMBAT = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "silver" = 3000) + build_path = /obj/item/ammo_casing/microbattery/combat/ion + sort_string = "MAVCD" + +/datum/design/item/nsfw_cell/shotstun + name = "SCATTERSTUN" + id = "nsfw_cell_shotstun" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 6, TECH_COMBAT = 6) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "silver" = 2000, "gold" = 2000) + build_path = /obj/item/ammo_casing/microbattery/combat/shotstun + sort_string = "MAVCE" + +/datum/design/item/nsfw_cell/xray + name = "XRAY" + id = "nsfw_cell_xray" + req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 4, TECH_POWER = 5, TECH_COMBAT = 7) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "silver" = 1000, "gold" = 1000, "uranium" = 1000, "phoron" = 1000) + build_path = /obj/item/ammo_casing/microbattery/combat/xray + sort_string = "MAVCF" + +/datum/design/item/nsfw_cell/stripper + name = "STRIPPER" + id = "nsfw_cell_stripper" + req_tech = list(TECH_MATERIAL = 7, TECH_BIO = 4, TECH_POWER = 4, TECH_COMBAT = 4, TECH_ILLEGAL = 5) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000, "uranium" = 2000, "phoron" = 2000, "diamond" = 500) + build_path = /obj/item/ammo_casing/microbattery/combat/stripper + sort_string = "MAVCG" + +/* +/datum/design/item/nsfw_cell/final + name = "FINAL OPTION" + id = "nsfw_cell_final" + req_tech = list(TECH_COMBAT = 69, TECH_ILLEGAL = 69, TECH_PRECURSOR = 1) + materials = list("unobtanium" = 9001) + build_path = /obj/item/ammo_casing/microbattery/combat/final + sort_string = "MAVCH" +*/ \ No newline at end of file diff --git a/code/modules/research/designs/xenoarch_toys.dm b/code/modules/research/designs/xenoarch_toys.dm new file mode 100644 index 0000000000..7c69090b1b --- /dev/null +++ b/code/modules/research/designs/xenoarch_toys.dm @@ -0,0 +1,31 @@ +/datum/design/item/weapon/xenoarch/AssembleDesignName() + ..() + name = "Xenoarcheology equipment design ([item_name])" + +// Xenoarch tools + +/datum/design/item/weapon/xenoarch/ano_scanner + name = "Alden-Saraspova counter" + id = "ano_scanner" + desc = "Aids in triangulation of exotic particles." + req_tech = list(TECH_BLUESPACE = 3, TECH_MAGNET = 3) + materials = list(DEFAULT_WALL_MATERIAL = 10000,"glass" = 5000) + build_path = /obj/item/device/ano_scanner + sort_string = "GAAAA" + +/datum/design/item/weapon/xenoarch/xenoarch_multi_tool + name = "xenoarcheology multitool" + id = "xenoarch_multitool" + req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3, TECH_BLUESPACE = 3) + build_path = /obj/item/device/xenoarch_multi_tool + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "uranium" = 500, "phoron" = 500) + sort_string = "GAAAB" + +/datum/design/item/weapon/xenoarch/excavationdrill + name = "Excavation Drill" + id = "excavationdrill" + req_tech = list(TECH_MATERIAL = 3, TECH_POWER = 2, TECH_ENGINEERING = 2, TECH_BLUESPACE = 3) + build_type = PROTOLATHE + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) + build_path = /obj/item/weapon/pickaxe/excavationdrill + sort_string = "GAAAC" \ No newline at end of file diff --git a/code/modules/research/designs/xenobio_toys.dm b/code/modules/research/designs/xenobio_toys.dm new file mode 100644 index 0000000000..917f8ba6c3 --- /dev/null +++ b/code/modules/research/designs/xenobio_toys.dm @@ -0,0 +1,30 @@ +/datum/design/item/weapon/xenobio/AssembleDesignName() + ..() + name = "Xenobiology equipment design ([item_name])" + +// Xenobio Weapons + +/datum/design/item/weapon/xenobio/slimebaton + id = "slimebaton" + req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2, TECH_POWER = 3, TECH_COMBAT = 3) + materials = list(DEFAULT_WALL_MATERIAL = 5000) + build_path = /obj/item/weapon/melee/baton/slime + sort_string = "HAAAA" + +/datum/design/item/weapon/xenobio/slimetaser + id = "slimetaser" + req_tech = list(TECH_MATERIAL = 3, TECH_BIO = 3, TECH_POWER = 4, TECH_COMBAT = 4) + materials = list(DEFAULT_WALL_MATERIAL = 5000) + build_path = /obj/item/weapon/gun/energy/taser/xeno + sort_string = "HAAAB" + +// Other + +/datum/design/item/weapon/xenobio/slime_scanner + name = "slime scanner" + desc = "A hand-held body scanner able to learn information about slimes." + id = "slime_scanner" + req_tech = list(TECH_MAGNET = 2, TECH_BIO = 2) + materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 500) + build_path = /obj/item/device/slime_scanner + sort_string = "HBAAA" \ No newline at end of file diff --git a/code/modules/research/designs_vr.dm b/code/modules/research/designs_vr.dm deleted file mode 100644 index 518c77a504..0000000000 --- a/code/modules/research/designs_vr.dm +++ /dev/null @@ -1,328 +0,0 @@ -/* Make language great again -/datum/design/item/implant/language - name = "Language implant" - id = "implant_language" - req_tech = list(TECH_MATERIAL = 5, TECH_BIO = 5, TECH_DATA = 4, TECH_ENGINEERING = 4) //This is not an easy to make implant. - materials = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 7000, "gold" = 2000, "diamond" = 3000) - build_path = /obj/item/weapon/implantcase/vrlanguage -*/ -/datum/design/item/implant/backup - name = "Backup implant" - id = "implant_backup" - req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2, TECH_DATA = 4, TECH_ENGINEERING = 2) - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 2000) - build_path = /obj/item/weapon/implantcase/backup - -/datum/design/item/implant/sizecontrol - name = "Size control implant" - id = "implant_size" - req_tech = list(TECH_MATERIAL = 3, TECH_BIO = 4, TECH_DATA = 4, TECH_ENGINEERING = 3) - materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 2000, "silver" = 3000) - build_path = /obj/item/weapon/implanter/sizecontrol - sort_string = "TAAAA" - -/datum/design/item/weapon/sizegun - name = "Size gun" - id = "sizegun" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 2000, "uranium" = 2000) - build_path = /obj/item/weapon/gun/energy/sizegun - sort_string = "TAAAB" - -/datum/design/item/bluespace_jumpsuit - name = "Bluespace jumpsuit" - id = "bsjumpsuit" - req_tech = list(TECH_BLUESPACE = 2, TECH_MATERIAL = 3, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) - build_path = /obj/item/clothing/under/bluespace - sort_string = "TAAAC" - -/datum/design/item/sleevemate - name = "SleeveMate 3700" - id = "sleevemate" - req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_BIO = 2) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) - build_path = /obj/item/device/sleevemate - sort_string = "TAAAD" - -/datum/design/item/bodysnatcher - name = "Body Snatcher" - id = "bodysnatcher" - req_tech = list(TECH_MAGNET = 3, TECH_BIO = 3, TECH_ILLEGAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) - build_path = /obj/item/device/bodysnatcher - -/datum/design/item/item/pressureinterlock - name = "APP pressure interlock" - id = "pressureinterlock" - req_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 250) - build_path = /obj/item/pressurelock - sort_string = "TAADA" - -/datum/design/item/weapon/advparticle - name = "Advanced anti-particle rifle" - id = "advparticle" - req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 5, TECH_POWER = 3, TECH_MAGNET = 3) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 1000, "gold" = 1000, "uranium" = 750) - build_path = /obj/item/weapon/gun/energy/particle/advanced - sort_string = "TAADB" - -/datum/design/item/weapon/particlecannon - name = "Anti-particle cannon" - id = "particlecannon" - req_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 5, TECH_POWER = 4, TECH_MAGNET = 4) - materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 1500, "gold" = 2000, "uranium" = 1000, "diamond" = 2000) - build_path = /obj/item/weapon/gun/energy/particle/cannon - sort_string = "TAADC" - -/datum/design/item/hud/omni - name = "AR glasses" - id = "omnihud" - req_tech = list(TECH_MAGNET = 4, TECH_COMBAT = 3, TECH_BIO = 3) - materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 1000) - build_path = /obj/item/clothing/glasses/omnihud - sort_string = "GAAFB" - -/datum/design/item/translocator - name = "Personal translocator" - id = "translocator" - req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_ILLEGAL = 6) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "uranium" = 4000, "diamond" = 2000) - build_path = /obj/item/device/perfect_tele - sort_string = "HABAF" - -/datum/design/item/nif - name = "nanite implant framework" - id = "nif" - req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5) - materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 8000, "uranium" = 6000, "diamond" = 6000) - build_path = /obj/item/device/nif - sort_string = "HABBC" - -/datum/design/item/nifbio - name = "bioadaptive NIF" - id = "bioadapnif" - req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5, TECH_BIO = 5) - materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 15000, "uranium" = 10000, "diamond" = 10000) - build_path = /obj/item/device/nif/bioadap - sort_string = "HABBD" //Changed String from HABBE to HABBD -//Addiing bioadaptive NIF to Protolathe - -/datum/design/item/nifrepairtool - name = "adv. NIF repair tool" - id = "anrt" - req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5) - materials = list(DEFAULT_WALL_MATERIAL = 200, "glass" = 3000, "uranium" = 2000, "diamond" = 2000) - build_path = /obj/item/device/nifrepairer - sort_string = "HABBE" //Changed String from HABBD to HABBE - -// Resleeving Circuitboards - -/datum/design/circuit/transhuman_clonepod - name = "grower pod" - id = "transhuman_clonepod" - req_tech = list(TECH_DATA = 3, TECH_BIO = 3) - build_path = /obj/item/weapon/circuitboard/transhuman_clonepod - sort_string = "HAADA" - -/datum/design/circuit/transhuman_synthprinter - name = "SynthFab 3000" - id = "transhuman_synthprinter" - req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) - build_path = /obj/item/weapon/circuitboard/transhuman_synthprinter - sort_string = "HAADB" - -/datum/design/circuit/transhuman_resleever - name = "Resleeving pod" - id = "transhuman_resleever" - req_tech = list(TECH_ENGINEERING = 4, TECH_BIO = 4) - build_path = /obj/item/weapon/circuitboard/transhuman_resleever - sort_string = "HAADC" - -/datum/design/circuit/resleeving_control - name = "Resleeving control console" - id = "resleeving_control" - req_tech = list(TECH_DATA = 5) - build_path = /obj/item/weapon/circuitboard/resleeving_control - sort_string = "HAADE" - -/datum/design/circuit/body_designer - name = "Body design console" - id = "body_designer" - req_tech = list(TECH_DATA = 5) - build_path = /obj/item/weapon/circuitboard/body_designer - sort_string = "HAADF" - -/datum/design/circuit/partslathe - name = "Parts lathe" - id = "partslathe" - req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2) - build_path = /obj/item/weapon/circuitboard/partslathe - sort_string = "HABAD" - -/datum/design/item/weapon/netgun - name = "\'Hunter\' capture gun" - id = "netgun" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 5, TECH_MAGNET = 3) - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 3000) - build_path = /obj/item/weapon/gun/energy/netgun - sort_string = "TAADF" - -/datum/design/circuit/algae_farm - name = "Algae Oxygen Generator" - id = "algae_farm" - req_tech = list(TECH_ENGINEERING = 3, TECH_BIO = 2) - build_path = /obj/item/weapon/circuitboard/algae_farm - sort_string = "HABAE" - -/datum/design/circuit/thermoregulator - name = "thermal regulator" - id = "thermoregulator" - req_tech = list(TECH_ENGINEERING = 4, TECH_POWER = 3) - build_path = /obj/item/weapon/circuitboard/thermoregulator - sort_string = "HABAF" - -/datum/design/circuit/bomb_tester - name = "Explosive Effect Simulator" - id = "bomb_tester" - req_tech = list(TECH_PHORON = 3, TECH_DATA = 2, TECH_MAGNET = 2) - build_path = /obj/item/weapon/circuitboard/bomb_tester - sort_string = "HABAG" - -/datum/design/circuit/quantum_pad - name = "Quantum Pad" - id = "quantum_pad" - req_tech = list(TECH_ENGINEERING = 4, TECH_POWER = 4, TECH_BLUESPACE = 4) - build_path = /obj/item/weapon/circuitboard/quantumpad - sort_string = "HABAH" - -//////Micro mech stuff -/datum/design/circuit/mecha/gopher_main - name = "'Gopher' central control" - id = "gopher_main" - build_path = /obj/item/weapon/circuitboard/mecha/gopher/main - sort_string = "NAAEA" - -/datum/design/circuit/mecha/gopher_peri - name = "'Gopher' peripherals control" - id = "gopher_peri" - build_path = /obj/item/weapon/circuitboard/mecha/gopher/peripherals - sort_string = "NAAEB" - -/datum/design/circuit/mecha/polecat_main - name = "'Polecat' central control" - id = "polecat_main" - req_tech = list(TECH_DATA = 4) - build_path = /obj/item/weapon/circuitboard/mecha/polecat/main - sort_string = "NAAFA" - -/datum/design/circuit/mecha/polecat_peri - name = "'Polecat' peripherals control" - id = "polecat_peri" - req_tech = list(TECH_DATA = 4) - build_path = /obj/item/weapon/circuitboard/mecha/polecat/peripherals - sort_string = "NAAFB" - -/datum/design/circuit/mecha/polecat_targ - name = "'Polecat' weapon control and targeting" - id = "polecat_targ" - req_tech = list(TECH_DATA = 4, TECH_COMBAT = 2) - build_path = /obj/item/weapon/circuitboard/mecha/polecat/targeting - sort_string = "NAAFC" - -/datum/design/circuit/mecha/weasel_main - name = "'Weasel' central control" - id = "weasel_main" - req_tech = list(TECH_DATA = 4) - build_path = /obj/item/weapon/circuitboard/mecha/weasel/main - sort_string = "NAAGA" - -/datum/design/circuit/mecha/weasel_peri - name = "'Weasel' peripherals control" - id = "weasel_peri" - req_tech = list(TECH_DATA = 4) - build_path = /obj/item/weapon/circuitboard/mecha/weasel/peripherals - sort_string = "NAAGB" - -/datum/design/circuit/mecha/weasel_targ - name = "'Weasel' weapon control and targeting" - id = "weasel_targ" - req_tech = list(TECH_DATA = 4, TECH_COMBAT = 2) - build_path = /obj/item/weapon/circuitboard/mecha/weasel/targeting - sort_string = "NAAGC" - -////// RIGSuit Stuff -/* -/datum/design/item/rig - req_tech = list(TECH_MATERIAL = 5, TECH_POWER = 5, TECH_MAGNET = 5) - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 6000, "uranium" = 4000) - -/datum/design/item/rig/eva - name = "eva hardsuit (empty)" - id = "eva_hardsuit" - build_path = /obj/item/weapon/rig/eva - sort_string = "HCAAA" - -/datum/design/item/rig/mining - name = "industrial hardsuit (empty)" - id = "ind_hardsuit" - build_path = /obj/item/weapon/rig/industrial - sort_string = "HCAAB" - -/datum/design/item/rig/research - name = "ami hardsuit (empty)" - id = "ami_hardsuit" - build_path = /obj/item/weapon/rig/hazmat - sort_string = "HCAAC" - -/datum/design/item/rig/medical - name = "medical hardsuit (empty)" - id = "med_hardsuit" - build_path = /obj/item/weapon/rig/medical - sort_string = "HCAAD" -*/ - -/datum/design/item/rig_module - req_tech = list(TECH_MATERIAL = 5, TECH_POWER = 5, TECH_MAGNET = 5) - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000) - -/datum/design/item/rig_module/plasma_cutter - name = "rig module - plasma cutter" - id = "rigmod_plasmacutter" - build_path = /obj/item/rig_module/device/plasmacutter - sort_string = "HCAAE" - -/datum/design/item/rig_module/diamond_drill - name = "rig module - diamond drill" - id = "rigmod_diamonddrill" - build_path = /obj/item/rig_module/device/drill - sort_string = "HCAAF" - -/datum/design/item/rig_module/maneuvering_jets - name = "rig module - maneuvering jets" - id = "rigmod_maneuveringjets" - build_path = /obj/item/rig_module/maneuvering_jets - sort_string = "HCAAG" - -/datum/design/item/rig_module/anomaly_scanner - name = "rig module - anomaly scanner" - id = "rigmod_anomalyscanner" - build_path = /obj/item/rig_module/device/anomaly_scanner - sort_string = "HCAAH" - -/datum/design/item/rig_module/orescanner - name = "rig module - ore scanner" - id = "rigmod_orescanner" - build_path = /obj/item/rig_module/device/orescanner - sort_string = "HCAAI" - -//Prosfab stuff for borgs and such - -/datum/design/item/robot_upgrade/sizeshift - name = "Size Alteration Module" - desc = "Used to allow robot to freely alter their size." - id = "borg_sizeshift_module" - req_tech = list(TECH_BLUESPACE = 3, TECH_MATERIAL = 3, TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) - build_path = /obj/item/borg/upgrade/sizeshift \ No newline at end of file diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm index bba633c6bc..0fdd3df5be 100644 --- a/code/modules/research/mechfab_designs.dm +++ b/code/modules/research/mechfab_designs.dm @@ -108,6 +108,12 @@ /datum/design/item/mechfab/gygax category = "Gygax" +/datum/design/item/mechfab/gygax/chassis/serenity + name = "Serenity Chassis" + id = "serenity_chassis" + build_path = /obj/item/mecha_parts/chassis/serenity + materials = list(DEFAULT_WALL_MATERIAL = 18750, "phoron" = 4000) + /datum/design/item/mechfab/gygax/chassis name = "Gygax Chassis" id = "gygax_chassis" @@ -564,6 +570,30 @@ materials = list(DEFAULT_WALL_MATERIAL = 8000, "gold" = 2000, "silver" = 3000, "phoron" = 5000, "glass" = 3750) build_path = /obj/item/mecha_parts/mecha_equipment/combat_shield +/datum/design/item/mecha/crisis_drone + name = "Crisis Drone" + desc = "Deploys a small medical drone capable of patching small wounds in order to stabilize nearby patients." + id = "mech_med_droid" + req_tech = list(TECH_PHORON = 3, TECH_MAGNET = 6, TECH_BIO = 5, TECH_DATA = 4, TECH_ARCANE = 1) + materials = list(DEFAULT_WALL_MATERIAL = 8000, MAT_GOLD = 2000, MAT_SILVER = 3000, MAT_VERDANTIUM = 2500, MAT_GLASS = 3000) + build_path = /obj/item/mecha_parts/mecha_equipment/crisis_drone + +/datum/design/item/mecha/rad_drone + name = "Hazmat Drone" + desc = "Deploys a small hazmat drone capable of purging minor radiation damage in order to stabilize nearby patients." + id = "mech_rad_droid" + req_tech = list(TECH_PHORON = 4, TECH_MAGNET = 5, TECH_BIO = 6, TECH_DATA = 4, TECH_ARCANE = 1) + materials = list(DEFAULT_WALL_MATERIAL = 8000, MAT_GOLD = 2000, MAT_URANIUM = 3000, MAT_VERDANTIUM = 2500, MAT_GLASS = 3000) + build_path = /obj/item/mecha_parts/mecha_equipment/crisis_drone/rad + +/datum/design/item/mecha/medanalyzer + name = "Mounted Body Scanner" + desc = "An advanced mech-mounted device that is not quite as powerful as a stationary body scanner, though still suitably powerful." + id = "mech_med_analyzer" + req_tech = list(TECH_PHORON = 4, TECH_MAGNET = 5, TECH_BIO = 5, TECH_DATA = 4) + materials = list(MAT_PLASTEEL = 4500, MAT_GOLD = 2000, MAT_URANIUM = 3000, MAT_GLASS = 3000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/medanalyzer + /datum/design/item/mecha/jetpack name = "Ion Jetpack" desc = "Using directed ion bursts and cunning solar wind reflection technique, this device enables controlled space flight." @@ -610,6 +640,46 @@ materials = list(DEFAULT_WALL_MATERIAL = 7500, "diamond" = 4875) build_path = /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill +/datum/design/item/mecha/ground_drill + name = "Surface Bore" + desc = "A heavy duty bore. Bigger, better, stronger than the core sampler, but not quite as good as a large drill." + id = "mech_ground_drill" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 2, TECH_PHORON = 1) + materials = list(DEFAULT_WALL_MATERIAL = 7000, "silver" = 3000, "phoron" = 2000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/drill/bore + +/datum/design/item/mecha/orescanner + name = "Ore Scanner" + desc = "A hefty device used to scan for subterranean veins of ore." + id = "mech_ore_scanner" + req_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/orescanner + +/datum/design/item/mecha/advorescanner + name = "Advanced Ore Scanner" + desc = "A hefty device used to scan for the exact volumes of subterranean veins of ore." + id = "mech_ore_scanner_adv" + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 4, TECH_POWER = 4, TECH_BLUESPACE = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "osmium" = 3000, "silver" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/orescanner/advanced + +/datum/design/item/mecha/powerwrench + name = "hydraulic wrench" + desc = "A large, hydraulic wrench." + id = "mech_wrench" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 5000, "plastic" = 2000, "glass" = 1250) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool + +/datum/design/item/mecha/powercrowbar + name = "hydraulic prybar" + desc = "A large, hydraulic prybar." + id = "mech_crowbar" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/prybar + /datum/design/item/mecha/generator_nuclear name = "Nuclear Reactor" desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy." @@ -618,6 +688,14 @@ materials = list(DEFAULT_WALL_MATERIAL = 7500, "silver" = 375, "glass" = 750) build_path = /obj/item/mecha_parts/mecha_equipment/generator/nuclear +/datum/design/item/mecha/speedboost_ripley + name = "Ripley Leg Actuator Overdrive" + desc = "System enhancements and overdrives to make a mech's legs move faster." + id = "mech_speedboost_ripley" + req_tech = list( TECH_POWER = 5, TECH_MATERIAL = 4, TECH_ENGINEERING = 4) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "silver" = 1000, "gold" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/speedboost + /datum/design/item/synthetic_flash name = "Synthetic Flash" id = "sflash" diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm index 305bae7df2..75c691dbd8 100644 --- a/code/modules/research/prosfab_designs.dm +++ b/code/modules/research/prosfab_designs.dm @@ -202,6 +202,21 @@ materials = list(DEFAULT_WALL_MATERIAL = 5625, "glass" = 1000) // req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2) +/datum/design/item/prosfab/pros/internal/spleen + name = "Prosthetic Spleen" + id = "pros_spleen" + build_path = /obj/item/organ/internal/spleen + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 3000, MAT_GLASS = 750) +// req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2) + +/datum/design/item/prosfab/pros/internal/larynx + name = "Prosthetic Larynx" + id = "pros_larynx" + build_path = /obj/item/organ/internal/voicebox + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_GLASS = 750, MAT_PLASTIC = 500) + //////////////////// Cyborg Parts //////////////////// /datum/design/item/prosfab/cyborg category = "Cyborg Parts" diff --git a/code/modules/research/prosfab_designs_vr.dm b/code/modules/research/prosfab_designs_vr.dm new file mode 100644 index 0000000000..02f39bd307 --- /dev/null +++ b/code/modules/research/prosfab_designs_vr.dm @@ -0,0 +1,8 @@ +//Prosfab stuff for borgs and such + +/datum/design/item/prosfab/robot_upgrade/sizeshift + name = "Size Alteration Module" + id = "borg_sizeshift_module" + req_tech = list(TECH_BLUESPACE = 3, TECH_MATERIAL = 3, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 4000) + build_path = /obj/item/borg/upgrade/sizeshift \ No newline at end of file diff --git a/code/modules/research/rigs_vr.dm b/code/modules/research/rigs_vr.dm new file mode 100644 index 0000000000..24c02d3fdd --- /dev/null +++ b/code/modules/research/rigs_vr.dm @@ -0,0 +1,123 @@ +/* + O - rigsuit stuff + OA - rigs themselves + OB - rig modules + OBAA - general purpose + OBAB - mining + OBAC - medical + OBAD - sec/combat + OBAE - engineering/maintenance/cleaning +*/ + + +////// RIGSuit Stuff +/* +/datum/design/item/rig + req_tech = list(TECH_MATERIAL = 5, TECH_POWER = 5, TECH_MAGNET = 5) + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 6000, "uranium" = 4000) + +/datum/design/item/rig/AssembleDesignName() + ..() + name = "hardsuit prototype ([name])" + +/datum/design/item/rig/eva + name = "eva hardsuit (empty)" + id = "eva_hardsuit" + build_path = /obj/item/weapon/rig/eva + sort_string = "OAAAA" + +/datum/design/item/rig/mining + name = "industrial hardsuit (empty)" + id = "ind_hardsuit" + build_path = /obj/item/weapon/rig/industrial + sort_string = "OAAAB" + +/datum/design/item/rig/research + name = "ami hardsuit (empty)" + id = "ami_hardsuit" + build_path = /obj/item/weapon/rig/hazmat + sort_string = "OAAAC" + +/datum/design/item/rig/medical + name = "medical hardsuit (empty)" + id = "med_hardsuit" + build_path = /obj/item/weapon/rig/medical + sort_string = "OAAAD" +*/ + +/datum/design/item/rig_module + req_tech = list(TECH_MATERIAL = 5, TECH_POWER = 5, TECH_MAGNET = 5) + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000) + +/datum/design/item/rig_module/AssembleDesignName() + ..() + name = "rig module prototype ([name])" + +/datum/design/item/rig_module/maneuvering_jets + name = "maneuvering jets" + id = "rigmod_maneuveringjets" + build_path = /obj/item/rig_module/maneuvering_jets + sort_string = "OBAAA" + +/datum/design/item/rig_module/sprinter + name = "sprinter" + id = "rigmod_sprinter" + build_path = /obj/item/rig_module/sprinter + sort_string = "OBAAB" + +/datum/design/item/rig_module/plasma_cutter + name = "plasma cutter" + id = "rigmod_plasmacutter" + build_path = /obj/item/rig_module/device/plasmacutter + sort_string = "OBABA" + +/datum/design/item/rig_module/diamond_drill + name = "diamond drill" + id = "rigmod_diamonddrill" + build_path = /obj/item/rig_module/device/drill + sort_string = "OBABB" + +/datum/design/item/rig_module/anomaly_scanner + name = "anomaly scanner" + id = "rigmod_anomalyscanner" + build_path = /obj/item/rig_module/device/anomaly_scanner + sort_string = "OBABC" + +/datum/design/item/rig_module/orescanner + name = "ore scanner" + id = "rigmod_orescanner" + build_path = /obj/item/rig_module/device/orescanner + sort_string = "OBABD" + +/datum/design/item/rig_module/rescue_pharm + name = "rescue pharm" + id = "rigmod_rescue_pharm" + build_path = /obj/item/rig_module/rescue_pharm + sort_string = "OBACA" + +/datum/design/item/rig_module/lasercannon + name = "laser cannon" + id = "rigmod_lasercannon" + build_path = /obj/item/rig_module/mounted + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000, "diamond" = 2000) + sort_string = "OBADA" + +/datum/design/item/rig_module/egun + name = "energy gun" + id = "rigmod_egun" + build_path = /obj/item/rig_module/mounted/egun + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000, "diamond" = 1000) + sort_string = "OBADB" + +/datum/design/item/rig_module/taser + name = "taser" + id = "rigmod_taser" + build_path = /obj/item/rig_module/mounted/taser + sort_string = "OBADC" + +/datum/design/item/rig_module/rcd + name = "rcd" + id = "rigmod_rcd" + build_path = /obj/item/rig_module/device/rcd + materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000, "diamond" = 2000) + sort_string = "OBAEA" \ No newline at end of file diff --git a/code/modules/research/teleport_vr.dm b/code/modules/research/teleport_vr.dm new file mode 100644 index 0000000000..5437e96bdd --- /dev/null +++ b/code/modules/research/teleport_vr.dm @@ -0,0 +1,23 @@ +/* + P - teleporteing item stuff +*/ + +/datum/design/item/teleport/AssembleDesignName() + ..() + name = "Teleportation device prototype ([item_name])" + +/datum/design/item/teleport/translocator + name = "Personal translocator" + id = "translocator" + req_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_ILLEGAL = 6) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "uranium" = 4000, "diamond" = 2000) + build_path = /obj/item/device/perfect_tele + sort_string = "PAAAA" + +/datum/design/item/teleport/bluespace_crystal + name = "Artificial Bluespace Crystal" + id = "bluespace_crystal" + req_tech = list(TECH_BLUESPACE = 3, TECH_PHORON = 4) + materials = list("diamond" = 1500, "phoron" = 1500) + build_path = /obj/item/weapon/ore/bluespace_crystal/artificial + sort_string = "PAAAB" \ No newline at end of file diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 65eb6dc363..5b7d7f2118 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -60,6 +60,9 @@ user.do_attack_animation(src) user.setClickCooldown(user.get_attack_speed()) +/obj/effect/energy_field/take_damage(var/damage) + adjust_strength(-damage / 20) + /obj/effect/energy_field/attack_hand(var/mob/living/user) impact_effect(3) // Harmless, but still produces the 'impact' effect. ..() diff --git a/code/modules/shuttles/shuttles_web.dm b/code/modules/shuttles/shuttles_web.dm index 3726ce23cd..d24f47b1d7 100644 --- a/code/modules/shuttles/shuttles_web.dm +++ b/code/modules/shuttles/shuttles_web.dm @@ -147,7 +147,7 @@ var/new_name = input(user, "Please enter a new name for this vessel. Note that you can only set its name once, so choose wisely.", "Rename Shuttle", visible_name) as null|text var/sanitized_name = sanitizeName(new_name, MAX_NAME_LEN, TRUE) if(sanitized_name) - can_rename = FALSE + //can_rename = FALSE //VOREStation Removal to_chat(user, "You've renamed the vessel to '[sanitized_name]'.") message_admins("[key_name_admin(user)] renamed shuttle '[visible_name]' to '[sanitized_name]'.") visible_name = sanitized_name diff --git a/code/modules/surgery/encased.dm b/code/modules/surgery/encased.dm index 3513691706..4e03024a01 100644 --- a/code/modules/surgery/encased.dm +++ b/code/modules/surgery/encased.dm @@ -134,7 +134,7 @@ if (!hasorgans(target)) return var/obj/item/organ/external/affected = target.get_organ(target_zone) - return ..() && affected && affected.open == 3 + return (..() && affected && affected.open == 3) /datum/surgery_step/open_encased/close/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) if (!hasorgans(target)) @@ -214,4 +214,94 @@ var/self_msg = "You applied \the [tool] to [target]'s [affected.encased]." user.visible_message(msg, self_msg) - affected.open = 2 \ No newline at end of file + affected.open = 2 + +/////////////////////////////////////////////////////////////// +// Saw/Retractor/Gel Combi-open and close. +/////////////////////////////////////////////////////////////// +/datum/surgery_step/open_encased/advancedsaw_open + allowed_tools = list( + /obj/item/weapon/surgical/circular_saw/manager = 100 + ) + + priority = 3 + + min_duration = 60 + max_duration = 90 + +/datum/surgery_step/open_encased/advancedsaw_open/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + return ..() && affected && affected.open >= 2 && affected.open < 3 + +/datum/surgery_step/open_encased/advancedsaw_open/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + user.visible_message("[user] begins to open [target]'s [affected.encased] with \the [tool].", \ + "You begin to open [target]'s [affected.encased] with \the [tool].") + target.custom_pain("Something hurts horribly in your [affected.name]!", 60) + ..() + +/datum/surgery_step/open_encased/advancedsaw_open/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + user.visible_message("[user] has cut [target]'s [affected.encased] wide open with \the [tool].", \ + "You have cut [target]'s [affected.encased] wide open with \the [tool].") + affected.open = 3 + +/datum/surgery_step/open_encased/advancedsaw_open/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + user.visible_message("[user]'s hand slips, searing [target]'s [affected.encased] with \the [tool]!" , \ + "Your hand slips, searing [target]'s [affected.encased] with \the [tool]!" ) + + affected.createwound(CUT, 20) + affected.createwound(BURN, 15) + if(prob(affected.damage)) + affected.fracture() + + +/datum/surgery_step/open_encased/advancedsaw_mend + allowed_tools = list( + /obj/item/weapon/surgical/circular_saw/manager = 100 + ) + + priority = 3 + + min_duration = 30 + max_duration = 60 + +/datum/surgery_step/open_encased/advancedsaw_mend/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + return (..() && affected && affected.open == 3) + +/datum/surgery_step/open_encased/advancedsaw_mend/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + var/msg = "[user] starts sealing \the [target]'s [affected.encased] with \the [tool]." + var/self_msg = "You start sealing \the [target]'s [affected.encased] with \the [tool]." + user.visible_message(msg, self_msg) + target.custom_pain("Something hurts horribly in your [affected.name]!", 100) + ..() + +/datum/surgery_step/open_encased/advancedsaw_mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + var/msg = "[user] sealed \the [target]'s [affected.encased] with \the [tool]." + var/self_msg = "You sealed \the [target]'s [affected.encased] with \the [tool]." + user.visible_message(msg, self_msg) + + affected.open = 2 diff --git a/code/modules/surgery/external_repair.dm b/code/modules/surgery/external_repair.dm index b26d803b34..4ed2d96c1a 100644 --- a/code/modules/surgery/external_repair.dm +++ b/code/modules/surgery/external_repair.dm @@ -9,8 +9,10 @@ req_open = 1 /datum/surgery_step/repairflesh/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if (target.stat == DEAD) // Sorry defibs, your subjects need to have pumping fluids for these to work. - return 0 +/* VOREStation Removal for Mlem Reasons(TM) + if (target.stat == DEAD) // Sorry defibs, your subjects need to have pumping fluids for these to work. + return 0 +*/ if (isslime(target)) return 0 if (target_zone == O_EYES || target_zone == O_MOUTH) @@ -34,7 +36,6 @@ /datum/surgery_step/repairflesh/scan_injury allowed_tools = list( /obj/item/weapon/autopsy_scanner = 100, - /obj/item/device/healthanalyzer = 80, /obj/item/device/analyzer = 10 ) @@ -85,8 +86,6 @@ /datum/surgery_step/repairflesh/repair_burns allowed_tools = list( /obj/item/stack/medical/advanced/ointment = 100, - /obj/item/weapon/surgical/FixOVein = 100, - /obj/item/weapon/surgical/hemostat = 60, /obj/item/stack/medical/ointment = 50, /obj/item/weapon/tape_roll = 30, /obj/item/taperoll = 10 @@ -153,8 +152,6 @@ /datum/surgery_step/repairflesh/repair_brute allowed_tools = list( /obj/item/stack/medical/advanced/bruise_pack = 100, - /obj/item/weapon/surgical/cautery = 100, - /obj/item/weapon/surgical/bonesetter = 60, /obj/item/stack/medical/bruise_pack = 50, /obj/item/weapon/tape_roll = 40, /obj/item/taperoll = 10 diff --git a/code/modules/surgery/neck.dm b/code/modules/surgery/neck.dm index 5b1522d60c..ec9803309f 100644 --- a/code/modules/surgery/neck.dm +++ b/code/modules/surgery/neck.dm @@ -24,7 +24,6 @@ priority = 1 allowed_tools = list( /obj/item/weapon/surgical/FixOVein = 100, - /obj/item/stack/nanopaste = 50, /obj/item/stack/cable_coil = 40, /obj/item/device/assembly/mousetrap = 5) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 2f2e1cb44a..8d261f7535 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -394,6 +394,65 @@ "Your hand slips, damaging the flesh in [target]'s [affected.name] with \the [tool]!") affected.createwound(BRUISE, 20) +/////////////////////////////////////////////////////////////// +// Organ Ripping Surgery +/////////////////////////////////////////////////////////////// + +/datum/surgery_step/internal/rip_organ + + allowed_tools = list( + /obj/item/weapon/surgical/scalpel/ripper = 100 + ) + + priority = 3 + + blood_level = 3 + + min_duration = 60 + max_duration = 80 + +/datum/surgery_step/internal/rip_organ/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!..()) + return 0 + + target.op_stage.current_organ = null + + var/list/removable_organs = list() + for(var/organ in target.internal_organs_by_name) + var/obj/item/organ/internal/I = target.internal_organs_by_name[organ] + if(istype(I) && I.parent_organ == target_zone) + removable_organs |= organ + + var/organ_to_remove = input(user, "Which organ do you want to remove?") as null|anything in removable_organs + if(!organ_to_remove) + return 0 + + target.op_stage.current_organ = organ_to_remove + return ..() + +/datum/surgery_step/internal/rip_organ/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + user.visible_message("[user] starts ripping [target]'s [target.op_stage.current_organ] out with \the [tool].", \ + "You start ripping [target]'s [target.op_stage.current_organ] out with \the [tool].") + target.custom_pain("Someone's ripping out your [target.op_stage.current_organ]!", 100) + ..() + +/datum/surgery_step/internal/rip_organ/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + user.visible_message("[user] has ripped [target]'s [target.op_stage.current_organ] out with \the [tool].", \ + "You have ripped [target]'s [target.op_stage.current_organ] out with \the [tool].") + + // Extract the organ! + if(target.op_stage.current_organ) + var/obj/item/organ/O = target.internal_organs_by_name[target.op_stage.current_organ] + if(O && istype(O)) + O.removed(user) + target.op_stage.current_organ = null + +/datum/surgery_step/internal/rip_organ/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message("[user]'s hand slips, damaging [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips, damaging [target]'s [affected.name] with \the [tool]!") + affected.createwound(BRUISE, 20) + ////////////////////////////////////////////////////////////////// // HEART SURGERY // ////////////////////////////////////////////////////////////////// diff --git a/code/modules/tables/presets.dm b/code/modules/tables/presets.dm index 06fca65138..085489b26a 100644 --- a/code/modules/tables/presets.dm +++ b/code/modules/tables/presets.dm @@ -58,6 +58,23 @@ material = get_material_by_name("wood") ..() +/obj/structure/table/sifwoodentable + icon_state = "plain_preview" + color = "#824B28" + +/obj/structure/table/sifwoodentable/New() + material = get_material_by_name("alien wood") + ..() + +/obj/structure/table/sifwooden_reinforced + icon_state = "reinf_preview" + color = "#824B28" + +/obj/structure/table/sifwooden_reinforced/New() + material = get_material_by_name("alien wood") + reinforced = get_material_by_name(DEFAULT_WALL_MATERIAL) + ..() + /obj/structure/table/gamblingtable icon_state = "gamble_preview" @@ -75,6 +92,15 @@ material = get_material_by_name("glass") ..() +/obj/structure/table/borosilicate + icon_state = "plain_preview" + color = "#4D3EAC" + alpha = 77 + +/obj/structure/table/borosilicate/New() + material = get_material_by_name("borosilicate glass") + ..() + /obj/structure/table/holotable icon_state = "holo_preview" color = "#EEEEEE" @@ -168,6 +194,18 @@ material = get_material_by_name("wood") ..() +/obj/structure/table/bench/sifwooden + icon_state = "plain_preview" + color = "#824B28" + +/obj/structure/table/bench/sifwooden/New() + material = get_material_by_name("alien wood") + ..() + +/obj/structure/table/bench/sifwooden/padded + icon_state = "padded_preview" + carpeted = 1 + /obj/structure/table/bench/padded icon_state = "padded_preview" diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm index 17d2a8e114..c5b7bde70e 100644 --- a/code/modules/tables/tables.dm +++ b/code/modules/tables/tables.dm @@ -26,6 +26,7 @@ var/list/table_icon_cache = list() // Gambling tables. I'd prefer reinforced with carpet/felt/cloth/whatever, but AFAIK it's either harder or impossible to get /obj/item/stack/material of those. // Convert if/when you can easily get stacks of these. var/carpeted = 0 + var/carpeted_type = /obj/item/stack/tile/carpet var/list/connections = list("nw0", "ne0", "sw0", "se0") @@ -43,7 +44,7 @@ var/list/table_icon_cache = list() health += maxhealth - old_maxhealth -/obj/structure/table/proc/take_damage(amount) +/obj/structure/table/take_damage(amount) // If the table is made of a brittle material, and is *not* reinforced with a non-brittle material, damage is multiplied by TABLE_BRITTLE_MATERIAL_MULTIPLIER if(material && material.is_brittle()) if(reinforced) @@ -110,7 +111,7 @@ var/list/table_icon_cache = list() if(carpeted && W.is_crowbar()) user.visible_message("\The [user] removes the carpet from \the [src].", "You remove the carpet from \the [src].") - new /obj/item/stack/tile/carpet(loc) + new carpeted_type(loc) carpeted = 0 update_icon() return 1 @@ -121,6 +122,7 @@ var/list/table_icon_cache = list() user.visible_message("\The [user] adds \the [C] to \the [src].", "You add \the [C] to \the [src].") carpeted = 1 + carpeted_type = W.type update_icon() return 1 else @@ -315,7 +317,7 @@ var/list/table_icon_cache = list() S = material.place_shard(loc) if(S) shards += S if(carpeted && (full_return || prob(50))) // Higher chance to get the carpet back intact, since there's no non-intact option - new /obj/item/stack/tile/carpet(src.loc) + new carpeted_type(src.loc) if(full_return || prob(20)) new /obj/item/stack/material/steel(src.loc) else diff --git a/code/modules/telesci/construction.dm b/code/modules/telesci/construction.dm index 9fc487f058..6fee75ab1b 100644 --- a/code/modules/telesci/construction.dm +++ b/code/modules/telesci/construction.dm @@ -30,43 +30,4 @@ /obj/item/weapon/ore/bluespace_crystal = 1, /obj/item/weapon/stock_parts/capacitor = 1, /obj/item/weapon/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 5) - -// The Designs - -/datum/design/circuit/telesci_console - name = "Telepad Control Console" - id = "telesci_console" - req_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 3, TECH_PHORON = 4) - build_path = /obj/item/weapon/circuitboard/telesci_console - sort_string = "HAAEA" - -/datum/design/circuit/telesci_pad - name = "Telepad" - id = "telesci_pad" - req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_PHORON = 4, TECH_BLUESPACE = 5) - build_path = /obj/item/weapon/circuitboard/telesci_pad - sort_string = "HAAEB" -/* Normal GPS has all the fancy features now -/datum/design/item/telesci_gps - name = "GPS device" - id = "telesci_gps" - req_tech = list(TECH_MATERIAL = 2, TECH_BLUESPACE = 2) - materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 1000) - build_path = /obj/item/device/gps/advanced - sort_string = "HAAEB" -*/ -/datum/design/circuit/quantum_pad - name = "Quantum Pad" - id = "quantum_pad" - req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_PHORON = 4, TECH_BLUESPACE = 5) - build_path = /obj/item/weapon/circuitboard/quantumpad - sort_string = "HAAC" - -/datum/design/item/bluespace_crystal - name = "Artificial Bluespace Crystal" - id = "bluespace_crystal" - req_tech = list(TECH_BLUESPACE = 3, TECH_PHORON = 4) - materials = list("diamond" = 1500, "phoron" = 1500) - build_path = /obj/item/weapon/ore/bluespace_crystal/artificial - sort_string = "HAAED" \ No newline at end of file + /obj/item/stack/cable_coil = 5) \ No newline at end of file diff --git a/code/modules/tension/tension.dm b/code/modules/tension/tension.dm index 82151a7e78..841d691037 100644 --- a/code/modules/tension/tension.dm +++ b/code/modules/tension/tension.dm @@ -69,7 +69,7 @@ var/friendly = threatened.faction == faction - var/threat = guess_threat_level() + var/threat = guess_threat_level(threatened) // Hurt entities contribute less tension. threat *= health @@ -99,7 +99,120 @@ return threat +// Carbon / mostly Human threat check. +/mob/living/carbon/get_threat(var/mob/living/threatened) + . = ..() + if(has_AI()) + if(!ai_holder.hostile) + return 0 + + if(incapacitated(INCAPACITATION_DISABLED)) + return 0 + + var/friendly = (IIsAlly(threatened) && a_intent == I_HELP) + + var/threat = guess_threat_level(threatened) + + threat *= health + threat /= getMaxHealth() + + // Allies reduce tension instead of adding. + if(friendly) + threat = -threat + + else + if(threatened.invisibility > see_invisible) + threat /= 2 // Target cannot be seen by src. + if(invisibility > threatened.see_invisible) + threat *= 2 // Target cannot see src. + + // Handle statuses. + if(confused) + threat /= 2 + + if(has_modifier_of_type(/datum/modifier/berserk)) + threat *= 2 + + return threat + +/mob/living/carbon/guess_threat_level(var/mob/living/threatened) + var/threat_guess = 0 + + // First lets consider their attack ability. + var/will_point_blank = FALSE + if(has_AI()) + will_point_blank = ai_holder.pointblank + + . = ..() + + var/obj/item/I = get_active_hand() + if(!I || !istype(I)) + var/damage_guess = 0 + if(ishuman(src) && ishuman(threatened)) + var/mob/living/carbon/human/H = src + var/datum/unarmed_attack/attack = H.get_unarmed_attack(threatened, BP_TORSO) + if(!attack) + damage_guess += 5 + + var/punch_damage = attack.get_unarmed_damage(H) + if(H.gloves) + if(istype(H.gloves, /obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = H.gloves + punch_damage += G.punch_force + + damage_guess += punch_damage + + else + damage_guess += 5 + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.outgoing_melee_damage_percent)) + damage_guess *= M.outgoing_melee_damage_percent + + threat_guess += damage_guess + + else + var/weapon_attack_speed = get_attack_speed(I) / (1 SECOND) + var/weapon_damage = I.force + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.outgoing_melee_damage_percent)) + weapon_damage *= M.outgoing_melee_damage_percent + + if(istype(I, /obj/item/weapon/gun)) + will_point_blank = TRUE + var/obj/item/weapon/gun/G = I + var/obj/item/projectile/P + + P = new G.projectile_type() + + if(P) // Does the gun even have a projectile type? + weapon_damage = P.damage + if(will_point_blank && a_intent == I_HURT) + weapon_damage *= 1.5 + weapon_attack_speed = G.fire_delay / (1 SECOND) + qdel(P) + + var/average_damage = weapon_damage / weapon_attack_speed + + threat_guess += average_damage + + // Consider intent. + switch(a_intent) + if(I_HELP) // Not likely to fight us. + threat_guess *= 0.4 + if(I_DISARM) // Might engage us, but unlikely to be with the intent to kill. + threat_guess *= 0.8 + if(I_GRAB) // May try to restrain us. This is here for reference, or later tweaking if needed. + threat_guess *= 1 + if(I_HURT) // May try to hurt us. + threat_guess *= 1.25 + + // Then consider their defense. + threat_guess += getMaxHealth() / 5 // 100 health translates to 20 threat. + + return threat_guess // Gives a rough idea of how much danger someone is in. Meant to be used for PvE things since PvP has too many unknown variables. /mob/living/proc/get_tension() diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index d761dbd6e8..8c626a6b46 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -59,6 +59,9 @@ set category = "Vehicle" set src in view(0) + if(!isliving(usr) || ismouse(usr)) + return + if(usr.incapacitated()) return if(!on && cell && cell.charge > charge_use) @@ -73,6 +76,9 @@ set category = "Vehicle" set src in view(0) + if(!isliving(usr) || ismouse(usr)) + return + if(usr.incapacitated()) return if(kickstand) diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm index 45cbcd7a13..46e838d37a 100644 --- a/code/modules/vehicles/cargo_train.dm +++ b/code/modules/vehicles/cargo_train.dm @@ -1,7 +1,7 @@ /obj/vehicle/train/engine name = "cargo train tug" desc = "A ridable electric car designed for pulling cargo trolleys." - icon = 'icons/obj/vehicles.dmi' + icon = 'icons/obj/vehicles_vr.dmi' //VOREStation Edit icon_state = "cargo_engine" on = 0 powered = 1 @@ -26,7 +26,7 @@ /obj/vehicle/train/trolley name = "cargo train trolley" - icon = 'icons/obj/vehicles.dmi' + icon = 'icons/obj/vehicles_vr.dmi' //VOREStation Edit icon_state = "cargo_trailer" anchored = 0 passenger_allowed = 0 @@ -34,7 +34,7 @@ load_item_visible = 1 load_offset_x = 0 - load_offset_y = 4 + load_offset_y = 7 //VOREStation Edit mob_offset_y = 8 //------------------------------------------- @@ -44,7 +44,7 @@ ..() cell = new /obj/item/weapon/cell/high(src) key = new key_type(src) - var/image/I = new(icon = 'icons/obj/vehicles.dmi', icon_state = "cargo_engine_overlay", layer = src.layer + 0.2) //over mobs + var/image/I = new(icon = 'icons/obj/vehicles_vr.dmi', icon_state = "cargo_engine_overlay", layer = src.layer + 0.2) //over mobs //VOREStation edit overlays += I turn_off() //so engine verbs are correctly set @@ -374,3 +374,17 @@ anchored = 0 else anchored = 1 + +// VOREStation Edit Start - Overlay stuff for the chair-like effect +/obj/vehicle/train/engine/update_icon() + ..() + overlays = null + var/image/O = image(icon = 'icons/obj/vehicles_vr.dmi', icon_state = "cargo_engine_overlay", dir = src.dir) + O.layer = FLY_LAYER + O.plane = MOB_PLANE + overlays += O + +/obj/vehicle/train/engine/set_dir() + ..() + update_icon() +// VOREStation Edit End - Overlay stuff for the chair-like effect diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 1bef7d41ea..87a1b72466 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -81,7 +81,6 @@ if(riding_datum) riding_datum.handle_ride(user, direction) - /obj/vehicle/Moved() . = ..() if(riding_datum) @@ -425,3 +424,12 @@ new /obj/effect/decal/cleanable/blood/oil(src.loc) spawn(1) healthcheck() return 1 + +/obj/vehicle/take_damage(var/damage) + if(!damage) + return + src.health -= damage + if(mechanical && prob(10)) + new /obj/effect/decal/cleanable/blood/oil(src.loc) + spawn(1) healthcheck() + return 1 diff --git a/code/modules/ventcrawl/ventcrawl.dm b/code/modules/ventcrawl/ventcrawl.dm index a306caea19..4728ed6ecf 100644 --- a/code/modules/ventcrawl/ventcrawl.dm +++ b/code/modules/ventcrawl/ventcrawl.dm @@ -18,6 +18,7 @@ var/list/ventcrawl_machinery = list( /mob/living/var/list/icon/pipes_shown = list() /mob/living/var/last_played_vent /mob/living/var/is_ventcrawling = 0 +/mob/living/var/prepping_to_ventcrawl = 0 /mob/var/next_play_vent = 0 /mob/living/proc/can_ventcrawl() @@ -106,7 +107,7 @@ var/list/ventcrawl_machinery = list( /mob/living/var/ventcrawl_layer = 3 /mob/living/proc/handle_ventcrawl(var/atom/clicked_on) - if(!can_ventcrawl()) + if(!can_ventcrawl() || prepping_to_ventcrawl) return var/obj/machinery/atmospherics/unary/vent_found @@ -153,6 +154,9 @@ var/list/ventcrawl_machinery = list( to_chat(src, "You feel a roaring wind pushing you away from the vent!") fade_towards(vent_found,45) + prepping_to_ventcrawl = 1 + spawn(50) + prepping_to_ventcrawl = 0 if(!do_after(src, 45, vent_found, 1, 1)) return if(!can_ventcrawl()) diff --git a/code/modules/virus2/disease2.dm b/code/modules/virus2/disease2.dm index 9c0c80c28d..89d470b0d1 100644 --- a/code/modules/virus2/disease2.dm +++ b/code/modules/virus2/disease2.dm @@ -39,6 +39,9 @@ spreadtype = prob(70) ? "Airborne" : "Contact" resistance = rand(15,70) + if(severity >= 2 && prob(33)) + resistance += 10 + if(all_species.len) affected_species = get_infectable_species() diff --git a/code/modules/vore/appearance/sprite_accessories_taur_vr.dm b/code/modules/vore/appearance/sprite_accessories_taur_vr.dm index 301d0841be..d4fd1f13dc 100644 --- a/code/modules/vore/appearance/sprite_accessories_taur_vr.dm +++ b/code/modules/vore/appearance/sprite_accessories_taur_vr.dm @@ -153,12 +153,18 @@ //Messages for smalls moving under larges var/msg_owner_stepunder = "%owner runs between your legs." //Weird becuase in the case this is used, %owner is the 'bumper' (src) var/msg_prey_stepunder = "You run between %prey's legs." //Same, inverse + hide_body_parts = list(BP_L_LEG, BP_L_FOOT, BP_R_LEG, BP_R_FOOT) //Exclude pelvis just in case. + clip_mask_icon = 'icons/mob/vore/taurs_vr.dmi' + clip_mask_state = "taur_clip_mask_def" //Used to clip off the lower part of suits & uniforms. /datum/sprite_accessory/tail/taur/roiz_long_lizard // Not ACTUALLY a taur, but it uses 32x64 so it wouldn't fit in tails.dmi, and having it as a tail bugs up the sprite. name = "Long Lizard Tail (Roiz Lizden)" icon_state = "roiz_tail_s" do_colouration = 0 ckeys_allowed = list("spoopylizz") + hide_body_parts = null + clip_mask_icon = null + clip_mask_state = null /datum/sprite_accessory/tail/taur/wolf name = "Wolf (Taur)" @@ -438,6 +444,7 @@ ckeys_allowed = list("natje") do_colouration = 0 can_ride = 0 + clip_mask_state = "taur_clip_mask_alraune" msg_prey_stepunder = "You run between %prey's vines." @@ -468,4 +475,26 @@ ckeys_allowed = null do_colouration = 1 extra_overlay = "alraunecolor_markings" - extra_overlay_w = "alraunecolor_closed_markings" \ No newline at end of file + extra_overlay_w = "alraunecolor_closed_markings" + clip_mask_state = "taur_clip_mask_alraune" + +/datum/sprite_accessory/tail/taur/wasp + name = "Wasp (dual color)" + icon_state = "wasp_s" + extra_overlay = "wasp_markings" + clip_mask_state = "taur_clip_mask_wasp" + + msg_owner_disarm_run = "You quickly push %prey to the ground with your leg!" + msg_prey_disarm_run = "%owner pushes you down to the ground with their leg!" + + msg_owner_disarm_walk = "You firmly push your leg down on %prey, painfully but harmlessly pinning them to the ground!" + msg_prey_disarm_walk = "%owner firmly pushes their leg down on you, quite painfully but harmlessly pinning you to the ground!" + + msg_owner_harm_walk = "You methodically place your leg down upon %prey's body, slowly applying pressure, crushing them against the floor!" + msg_prey_harm_walk = "%owner methodically places their leg upon your body, slowly applying pressure, crushing you against the floor!" + + msg_owner_grab_success = "You pin %prey down on the ground with your front leg before using your other leg to pick them up, trapping them between two of your front legs!" + msg_prey_grab_success = "%owner pins you down on the ground with their front leg before using their other leg to pick you up, trapping you between two of their front legs!" + + msg_owner_grab_fail = "You step down onto %prey, squishing them and forcing them down to the ground!" + msg_prey_grab_fail = "%owner steps down and squishes you with their leg, forcing you down to the ground!" diff --git a/code/modules/vore/appearance/sprite_accessories_vr.dm b/code/modules/vore/appearance/sprite_accessories_vr.dm index ea2bae23c3..efdb36d628 100644 --- a/code/modules/vore/appearance/sprite_accessories_vr.dm +++ b/code/modules/vore/appearance/sprite_accessories_vr.dm @@ -653,6 +653,9 @@ var/desc = "You should not see this..." var/ani_state // State when wagging/animated var/extra_overlay_w // Wagging state for extra overlay + var/list/hide_body_parts = list() //Uses organ tag defines. Bodyparts in this list do not have their icons rendered, allowing for more spriter freedom when doing taur/digitigrade stuff. + var/icon/clip_mask_icon = null //Icon file used for clip mask. + var/clip_mask_state = null //Icon state to generate clip mask. Clip mask is used to 'clip' off the lower part of clothing such as jumpsuits & full suits. /datum/sprite_accessory/tail/invisible name = "hide species-sprite tail" @@ -919,6 +922,12 @@ ani_state = "ketraitail_w" //ckeys_allowed = list("ketrai") //They requested it to be enabled for everyone. +/datum/sprite_accessory/tail/ketrainew_wag + name = "new fennix tail (vwag)" + desc = "" + icon_state = "ketraitailnew" + ani_state = "ketraitailnew_w" + /datum/sprite_accessory/tail/redpanda name = "red panda" desc = "" @@ -944,6 +953,9 @@ icon_state = "satyr" color_blend_mode = ICON_MULTIPLY do_colouration = 1 + hide_body_parts = list(BP_L_LEG, BP_L_FOOT, BP_R_LEG, BP_R_FOOT) //Exclude pelvis just in case. + clip_mask_icon = 'icons/mob/vore/taurs_vr.dmi' + clip_mask_state = "taur_clip_mask_def" //Used to clip off the lower part of suits & uniforms. /datum/sprite_accessory/tail/tailmaw name = "tailmaw, colorable" @@ -970,6 +982,16 @@ do_colouration = 1 color_blend_mode = ICON_MULTIPLY +/datum/sprite_accessory/tail/sneptail + name = "Snep/Furry Tail (vwag)" + desc = "" + icon_state = "sneptail" + ani_state = "sneptail_w" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + extra_overlay = "sneptail_mark" + extra_overlay_w = "sneptail_mark_w" + /datum/sprite_accessory/tail/tiger_new name = "tiger tail (vwag)" desc = "" @@ -1512,4 +1534,12 @@ do_colouration = 1 color_blend_mode = ICON_MULTIPLY extra_overlay = "buggofirefly_vass_markings" - extra_overlay_w = "buggofatfirefly_vass_markings" \ No newline at end of file + extra_overlay_w = "buggofatfirefly_vass_markings" + +/datum/sprite_accessory/tail/tail_smooth + name = "Smooth Lizard Tail, Colorable" + desc = "" + icon_state = "tail_smooth" + ani_state = "tail_smooth_w" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY \ No newline at end of file diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index 6547aacd23..faf2d43d66 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -1,4 +1,5 @@ #define VORE_SOUND_FALLOFF 0.1 +#define VORE_SOUND_RANGE 3 // // Belly system 2.0, now using objects instead of datums because EH at datums. @@ -20,25 +21,28 @@ var/emote_time = 60 SECONDS // How long between stomach emotes at prey var/digest_brute = 2 // Brute damage per tick in digestion mode var/digest_burn = 2 // Burn damage per tick in digestion mode - var/immutable = 0 // Prevents this belly from being deleted - var/escapable = 0 // Belly can be resisted out of at any time + var/immutable = FALSE // Prevents this belly from being deleted + var/escapable = FALSE // Belly can be resisted out of at any time var/escapetime = 60 SECONDS // Deciseconds, how long to escape this belly var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles var/absorbchance = 0 // % Chance of stomach beginning to absorb if prey struggles var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles. var/transferchance = 0 // % Chance of prey being - var/can_taste = 0 // If this belly prints the flavor of prey when it eats someone. + var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone. var/bulge_size = 0.25 // The minimum size the prey has to be in order to show up on examine. var/shrink_grow_size = 1 // This horribly named variable determines the minimum/maximum size it will shrink/grow prey to. var/transferlocation // Location that the prey is released if they struggle and get dropped off. - var/release_sound = TRUE // Boolean for now, maybe replace with something else later + var/release_sound = "Splatter" // Sound for letting someone out. Replaced from True/false var/mode_flags = 0 // Stripping, numbing, etc. + var/fancy_vore = FALSE // Using the new sounds? + var/is_wet = TRUE // Is this belly's insides made of slimy parts? + var/wet_loop = TRUE // Does the belly have a fleshy loop playing? //I don't think we've ever altered these lists. making them static until someone actually overrides them somewhere. //Actual full digest modes var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_ABSORB,DM_DRAIN,DM_UNABSORB,DM_HEAL,DM_SHRINK,DM_GROW,DM_SIZE_STEAL) //Digest mode addon flags - var/tmp/static/list/mode_flag_list = list("Numbing" = DM_FLAG_NUMBING, "Stripping" = DM_FLAG_STRIPPING, "Leave Remains" = DM_FLAG_LEAVEREMAINS) + var/tmp/static/list/mode_flag_list = list("Numbing" = DM_FLAG_NUMBING, "Stripping" = DM_FLAG_STRIPPING, "Leave Remains" = DM_FLAG_LEAVEREMAINS, "Muffles" = DM_FLAG_THICKBELLY) //Transformation modes var/tmp/static/list/transform_modes = list(DM_TRANSFORM_MALE,DM_TRANSFORM_FEMALE,DM_TRANSFORM_KEEP_GENDER,DM_TRANSFORM_CHANGE_SPECIES_AND_TAUR,DM_TRANSFORM_CHANGE_SPECIES_AND_TAUR_EGG,DM_TRANSFORM_REPLICA,DM_TRANSFORM_REPLICA_EGG,DM_TRANSFORM_KEEP_GENDER_EGG,DM_TRANSFORM_MALE_EGG,DM_TRANSFORM_FEMALE_EGG, DM_EGG) //Item related modes @@ -54,6 +58,7 @@ var/tmp/list/items_preserved = list() // Stuff that wont digest so we shouldn't process it again. var/tmp/next_emote = 0 // When we're supposed to print our next emote, as a belly controller tick # var/tmp/recent_sound = FALSE // Prevent audio spam + var/tmp/list/hearing_mobs // Don't forget to watch your commas at the end of each line if you change these. var/list/struggle_messages_outside = list( @@ -147,7 +152,11 @@ "item_digest_mode", "contaminates", "contamination_flavor", - "contamination_color" + "contamination_color", + "release_sound", + "fancy_vore", + "is_wet", + "wet_loop" ) /obj/belly/New(var/newloc) @@ -175,7 +184,11 @@ //Sound w/ antispam flag setting if(vore_sound && !recent_sound) - var/soundfile = vore_sounds[vore_sound] + var/soundfile + if(!fancy_vore) + soundfile = classic_vore_sounds[vore_sound] + else + soundfile = fancy_vore_sounds[vore_sound] if(soundfile) playsound(src, soundfile, vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/eating_noises) recent_sound = TRUE @@ -196,7 +209,7 @@ //Don't bother if we don't have contents if(!contents.len) - return 0 + return FALSE //Find where we should drop things into (certainly not the owner) var/count = 0 @@ -218,8 +231,13 @@ //Print notifications/sound if necessary if(!silent) owner.visible_message("[owner] expels everything from their [lowertext(name)]!") - if(release_sound) - playsound(src, 'sound/effects/splat.ogg', vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/eating_noises) + var/soundfile + if(!fancy_vore) + soundfile = classic_release_sounds[release_sound] + else + soundfile = fancy_release_sounds[release_sound] + if(soundfile) + playsound(src, soundfile, vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/eating_noises) return count @@ -232,12 +250,17 @@ //Place them into our drop_location M.forceMove(drop_location()) + items_preserved -= M //Special treatment for absorbed prey - if(istype(M,/mob/living)) + if(isliving(M)) var/mob/living/ML = M var/mob/living/OW = owner + if(ML.client) + ML.stop_sound_channel(CHANNEL_PREYLOOP) //Stop the internal loop, it'll restart if the isbelly check on next tick anyway + if(ML.muffled) + ML.muffled = 0 if(ML.absorbed) ML.absorbed = FALSE if(ishuman(M) && ishuman(OW)) @@ -256,8 +279,13 @@ //Print notifications/sound if necessary if(!silent) owner.visible_message("[owner] expels [M] from their [lowertext(name)]!") - if(release_sound) - playsound(src, 'sound/effects/splat.ogg', vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/eating_noises) + var/soundfile + if(!fancy_vore) + soundfile = classic_release_sounds[release_sound] + else + soundfile = fancy_release_sounds[release_sound] + if(soundfile) + playsound(src, soundfile, vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/eating_noises) return 1 @@ -401,6 +429,8 @@ else if(M.reagents) M.reagents.trans_to_holder(Pred.bloodstr, M.reagents.total_volume, 0.5, TRUE) + //Incase they have the loop going, let's double check to stop it. + M.stop_sound_channel(CHANNEL_PREYLOOP) // Delete the digested mob qdel(M) @@ -513,9 +543,17 @@ M.show_message(struggle_outer_message, 2) // hearable to_chat(R,struggle_user_message) - var/strpick = pick(struggle_sounds) - var/strsound = struggle_sounds[strpick] - playsound(src, strsound, vary = 1, vol = 100, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/digestion_noises) + var/sound/struggle_snuggle + var/sound/struggle_rustle = sound(get_sfx("rustle")) + + if(is_wet) + if(!fancy_vore) + struggle_snuggle = sound(get_sfx("classic_struggle_sounds")) + else + struggle_snuggle = sound(get_sfx("fancy_prey_struggle")) + playsound(src, struggle_snuggle, vary = 1, vol = 75, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/digestion_noises) + else + playsound(src, struggle_rustle, vary = 1, vol = 75, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/digestion_noises) if(escapable) //If the stomach has escapable enabled. if(prob(escapechance)) //Let's have it check to see if the prey escapes first. @@ -599,7 +637,11 @@ I.gurgle_contaminate(target.contents, target.contamination_flavor, target.contamination_color) items_preserved -= content if(!silent && target.vore_sound && !recent_sound) - var/soundfile = vore_sounds[target.vore_sound] + var/soundfile + if(!fancy_vore) + soundfile = classic_vore_sounds[target.vore_sound] + else + soundfile = fancy_vore_sounds[target.vore_sound] if(soundfile) playsound(src, soundfile, vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, preference = /datum/client_preference/digestion_noises) owner.updateVRPanel() @@ -637,6 +679,10 @@ dupe.contaminates = contaminates dupe.contamination_flavor = contamination_flavor dupe.contamination_color = contamination_color + dupe.release_sound = release_sound + dupe.fancy_vore = fancy_vore + dupe.is_wet = is_wet + dupe.wet_loop = wet_loop //// Object-holding variables //struggle_messages_outside - strings diff --git a/code/modules/vore/eating/bellymodes_vr.dm b/code/modules/vore/eating/bellymodes_vr.dm index 886ca5830c..e879eeddd5 100644 --- a/code/modules/vore/eating/bellymodes_vr.dm +++ b/code/modules/vore/eating/bellymodes_vr.dm @@ -24,6 +24,35 @@ if(M.digestable || digest_mode != DM_DIGEST) // don't give digesty messages to indigestible people to_chat(M,"[pick(EL)]") +///////////////////// Prey Loop Refresh/hack ////////////////////// + for(var/mob/living/M in contents) + M.stop_sound_channel(CHANNEL_PREYLOOP) // sanity just in case, because byond is whack and you can't trust it + if(isbelly(M.loc)) //sanity check + if(world.time > M.next_preyloop) //We don't want it to overlap, but we also want it to replay. + if(is_wet && wet_loop) // Is it a fleshy environment, and does the pred have a fleshy heartbeat loop to play? + if(!M.client) + continue + if(M.is_preference_enabled(/datum/client_preference/digestion_noises)) //then we check if the mob has sounds enabled at all + var/sound/preyloop = sound('sound/vore/sunesound/prey/loop.ogg') + M.playsound_local(get_turf(src),preyloop, 80,0, channel = CHANNEL_PREYLOOP) + M.next_preyloop = (world.time + (52 SECONDS)) + +/////////////////////////// Sound Selections /////////////////////////// + var/sound/prey_digest + var/sound/prey_death + var/sound/pred_digest + var/sound/pred_death + if(!fancy_vore) + prey_digest = sound(get_sfx("classic_digestion_sounds")) + prey_death = sound(get_sfx("classic_death_sounds")) + pred_digest = sound(get_sfx("classic_digestion_sounds")) + pred_death = sound(get_sfx("classic_death_sounds")) + else + prey_digest = sound(get_sfx("fancy_digest_prey")) + prey_death = sound(get_sfx("fancy_death_prey")) + pred_digest = sound(get_sfx("fancy_digest_pred")) + pred_death = sound(get_sfx("fancy_death_pred")) + /////////////////////////// Exit Early //////////////////////////// var/list/touchable_atoms = contents - items_preserved if(!length(touchable_atoms)) @@ -47,7 +76,7 @@ else items_preserved |= I if(prob(25)) //Less often than with normal digestion - play_sound = pick(digestion_sounds) + play_sound = pick(pred_digest) else if(item_digest_mode == IM_DIGEST) if(I.digest_stage && I.digest_stage > 0) digest_item(I) @@ -56,7 +85,7 @@ did_an_item = TRUE to_update = TRUE if(prob(25)) //Less often than with normal digestion - play_sound = pick(digestion_sounds) + play_sound = pick(pred_digest) //Handle eaten mobs else if(isliving(A)) @@ -75,6 +104,11 @@ if(H.bloodstr.get_reagent_amount("numbenzyme") < 2) H.bloodstr.add_reagent("numbenzyme",4) + //Thickbelly flag + if(mode_flags & DM_FLAG_THICKBELLY) + if(!(H.muffled)) + H.muffled = 1 + //Stripping flag if(mode_flags & DM_FLAG_STRIPPING) for(var/slot in slots) @@ -91,11 +125,15 @@ else items_preserved |= I if(prob(25)) //Less often than with normal digestion - play_sound = pick(digestion_sounds) + if(L && L.client && L.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(L,prey_digest) + play_sound = pick(pred_digest) else if(item_digest_mode == IM_DIGEST) digest_item(I) if(prob(25)) //Less often than with normal digestion - play_sound = pick(digestion_sounds) + if(L && L.client && L.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(L,prey_digest) + play_sound = pick(pred_digest) to_update = TRUE break //get rid of things like blood drops and gibs that end up in there @@ -110,7 +148,10 @@ else if(digest_mode == DM_DIGEST) if(prob(50)) //Was SO OFTEN. AAAA. - play_sound = pick(digestion_sounds) + for(var/mob/M in contents) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) for (var/target in touchable_mobs) var/mob/living/M = target @@ -137,7 +178,9 @@ to_chat(owner,"" + digest_alert_owner + "") to_chat(M,"" + digest_alert_prey + "") - play_sound = pick(death_sounds) + play_sound = pick(pred_death) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_death) if((mode_flags & DM_FLAG_LEAVEREMAINS) && M.digest_leave_remains) handle_remains_leaving(M) digestion_death(M) @@ -178,7 +221,9 @@ for (var/target in touchable_mobs) var/mob/living/M = target if(prob(10)) //Less often than gurgles. People might leave this on forever. - play_sound = pick(digestion_sounds) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) if(M.absorbed) continue @@ -211,7 +256,9 @@ var/mob/living/M = target if(prob(10)) //Less often than gurgles. People might leave this on forever. - play_sound = pick(digestion_sounds) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) if(M.nutrition >= 100) //Drain them until there's no nutrients left. var/oldnutrition = (M.nutrition * 0.05) @@ -225,7 +272,9 @@ var/mob/living/M = target if(prob(10)) //Infinite gurgles! - play_sound = pick(digestion_sounds) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) if(M.size_multiplier > shrink_grow_size) //Shrink until smol. M.resize(M.size_multiplier-0.01) //Shrink by 1% per tick. @@ -242,7 +291,9 @@ var/mob/living/M = target if(prob(10)) - play_sound = pick(digestion_sounds) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) if(M.size_multiplier < shrink_grow_size) //Grow until large. M.resize(M.size_multiplier+0.01) //Grow by 1% per tick. @@ -256,7 +307,9 @@ var/mob/living/M = target if(prob(10)) - play_sound = pick(digestion_sounds) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) if(M.size_multiplier > shrink_grow_size && owner.size_multiplier < 2) //Grow until either pred is large or prey is small. owner.resize(owner.size_multiplier+0.01) //Grow by 1% per tick. @@ -271,7 +324,10 @@ else if(digest_mode == DM_HEAL) if(prob(50)) //Wet heals! The secret is you can leave this on for gurgle noises for fun. - play_sound = pick(digestion_sounds) + for(var/mob/M in contents) + if(M && M.client && M.is_preference_enabled(/datum/client_preference/digestion_noises)) + SEND_SOUND(M,prey_digest) + play_sound = pick(pred_digest) for (var/target in touchable_mobs) var/mob/living/M = target @@ -298,7 +354,18 @@ /////////////////////////// Make any noise /////////////////////////// if(play_sound) - playsound(src, play_sound, vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF, ignore_walls = TRUE, preference = /datum/client_preference/digestion_noises) + LAZYCLEARLIST(hearing_mobs) + for(var/mob/M in hearers(VORE_SOUND_RANGE, owner)) + if(!M.client || !(M.is_preference_enabled(/datum/client_preference/digestion_noises))) + continue + LAZYADD(hearing_mobs, M) + for(var/mob/M in hearing_mobs) //so we don't fill the whole room with the sound effect + if(M && M.client && (isturf(M.loc) || (M.loc != src.contents))) //to avoid people on the inside getting the outside sounds and their direct sounds + built in sound pref check + if(fancy_vore) + M.playsound_local(owner.loc, play_sound, vol = 75, vary = 1, falloff = VORE_SOUND_FALLOFF) + else + M.playsound_local(owner.loc, play_sound, vol = 100, vary = 1, falloff = VORE_SOUND_FALLOFF) + //these are all external sound triggers now, so it's ok. if(to_update) for(var/mob/living/M in contents) if(M.client) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 53b7a4d9ed..9d8e712b2d 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -1,12 +1,14 @@ ///////////////////// Mob Living ///////////////////// /mob/living - var/digestable = 1 // Can the mob be digested inside a belly? - var/digest_leave_remains = 0 // Will this mob leave bones/skull/etc after the melty demise? - var/allowmobvore = 1 // Will simplemobs attempt to eat the mob? - var/showvoreprefs = 1 // Determines if the mechanical vore preferences button will be displayed on the mob or not. + var/digestable = TRUE // Can the mob be digested inside a belly? + var/devourable = TRUE // Can the mob be devoured at all? + var/feeding = TRUE // Can the mob be vorishly force fed or fed to others? + var/digest_leave_remains = FALSE // Will this mob leave bones/skull/etc after the melty demise? + var/allowmobvore = TRUE // Will simplemobs attempt to eat the mob? + var/showvoreprefs = TRUE // Determines if the mechanical vore preferences button will be displayed on the mob or not. var/obj/belly/vore_selected // Default to no vore capability. var/list/vore_organs = list() // List of vore containers inside a mob - var/absorbed = 0 // If a mob is absorbed into another + var/absorbed = FALSE // If a mob is absorbed into another var/weight = 137 // Weight for mobs for weightgain system var/weight_gain = 1 // How fast you gain weight var/weight_loss = 0.5 // How fast you lose weight @@ -15,15 +17,17 @@ var/revive_ready = REVIVING_READY // Only used for creatures that have the xenochimera regen ability, so far. var/metabolism = 0.0015 var/vore_taste = null // What the character tastes like - var/no_vore = 0 // If the character/mob can vore. - var/openpanel = 0 // Is the vore panel open? - var/noisy = 0 // Toggle audible hunger. + var/no_vore = FALSE // If the character/mob can vore. + var/openpanel = FALSE // Is the vore panel open? + var/noisy = FALSE // Toggle audible hunger. var/absorbing_prey = 0 // Determines if the person is using the succubus drain or not. See station_special_abilities_vr. var/drain_finalized = 0 // Determines if the succubus drain will be KO'd/absorbed. Can be toggled on at any time. var/fuzzy = 1 // Preference toggle for sharp/fuzzy icon. var/tail_alt = 0 // Tail layer toggle. - var/can_be_drop_prey = 0 - var/can_be_drop_pred = 1 // Mobs are pred by default. + var/permit_healbelly = TRUE + var/can_be_drop_prey = FALSE + var/can_be_drop_pred = TRUE // Mobs are pred by default. + var/next_preyloop // For Fancy sound internal loop // // Hook for generic creation of stuff on new creatures @@ -33,7 +37,7 @@ M.verbs += /mob/living/proc/lick M.verbs += /mob/living/proc/switch_scaling if(M.no_vore) //If the mob isn't supposed to have a stomach, let's not give it an insidepanel so it can make one for itself, or a stomach. - return 1 + return TRUE M.verbs += /mob/living/proc/insidePanel //Tries to load prefs if a client is present otherwise gives freebie stomach @@ -41,8 +45,8 @@ if(M) M.init_vore() - //Return 1 to hook-caller - return 1 + //return TRUE to hook-caller + return TRUE /mob/living/proc/init_vore() //Something else made organs, meanwhile. @@ -63,10 +67,10 @@ LAZYINITLIST(vore_organs) var/obj/belly/B = new /obj/belly(src) vore_selected = B - B.immutable = 1 + B.immutable = TRUE B.name = "Stomach" B.desc = "It appears to be rather warm and wet. Makes sense, considering it's inside \the [name]." - B.can_taste = 1 + B.can_taste = TRUE return TRUE // @@ -91,29 +95,45 @@ var/mob/living/attacker = user // Typecast to living - // src is the mob clicked on + // src is the mob clicked on and attempted predator - ///// If grab clicked on grabber + ///// If user clicked on themselves if((src == G.assailant) && (is_vore_predator(src))) - if (src.feed_grabbed_to_self(src, G.affecting)) + if(!(G.affecting.devourable)) + to_chat(user, "They aren't able to be devoured.") + return FALSE + if(src.feed_grabbed_to_self(src, G.affecting)) qdel(G) - return 1 + return TRUE else log_debug("[attacker] attempted to feed [G.affecting] to [user] ([user.type]) but it failed.") - ///// If grab clicked on grabbed + ///// If user clicked on their grabbed target else if((src == G.affecting) && (attacker.a_intent == I_GRAB) && (attacker.zone_sel.selecting == BP_TORSO) && (is_vore_predator(G.affecting))) + if(!(G.affecting.feeding)) + to_chat(user, "[G.affecting] isn't willing to be fed.") + return FALSE if (attacker.feed_self_to_grabbed(attacker, G.affecting)) qdel(G) - return 1 + return TRUE else log_debug("[attacker] attempted to feed [user] to [G.affecting] ([G.affecting.type]) but it failed.") - ///// If grab clicked on anyone else + ///// If user clicked on anyone else but their grabbed target else if((src != G.affecting) && (src != G.assailant) && (is_vore_predator(src))) + if(!(src.feeding)) + to_chat(user, "[src] isn't willing to be fed.") + return FALSE + if(!(G.affecting.devourable)) + to_chat(user, "[G.affecting] isn't able to be devoured.") + return FALSE + if(!(G.affecting.feeding)) + to_chat(user, "[src] isn't able to be fed to someone.") + return FALSE + if (attacker.feed_grabbed_to_other(attacker, G.affecting, src)) qdel(G) - return 1 + return TRUE else log_debug("[attacker] attempted to feed [G.affecting] to [src] ([src.type]) but it failed.") @@ -121,7 +141,7 @@ else if(istype(I,/obj/item/weapon/holder)) var/obj/item/weapon/holder/H = I - if(!isliving(user)) return 0 // Return 0 to continue upper procs + if(!isliving(user)) return FALSE // return FALSE to continue upper procs var/mob/living/attacker = user // Typecast to living if (is_vore_predator(src)) @@ -129,7 +149,7 @@ if (attacker.eat_held_mob(attacker, M, src)) if (H.held_mob == M) H.held_mob = null - return 1 //Return 1 to exit upper procs + return TRUE //return TRUE to exit upper procs else log_debug("[attacker] attempted to feed [H.contents] to [src] ([src.type]) but it failed.") @@ -139,16 +159,16 @@ if(confirm == "Yes!") var/obj/belly/B = input("Which belly?","Select A Belly") as null|anything in vore_organs if(!istype(B)) - return 1 + return TRUE visible_message("[user] is trying to stuff a beacon into [src]'s [lowertext(B.name)]!","[user] is trying to stuff a beacon into you!") if(do_after(user,30,src)) user.drop_item() I.forceMove(B) - return 1 + return TRUE else - return 1 //You don't get to hit someone 'later' + return TRUE //You don't get to hit someone 'later' - return 0 + return FALSE // // Our custom resist catches for /mob/living @@ -163,42 +183,45 @@ //Other overridden resists go here - return 0 + return FALSE // // Verb for saving vore preferences to save file // /mob/living/proc/save_vore_prefs() if(!client || !client.prefs_vr) - return 0 + return FALSE if(!copy_to_prefs_vr()) - return 0 + return FALSE if(!client.prefs_vr.save_vore()) - return 0 + return FALSE - return 1 + return TRUE /mob/living/proc/apply_vore_prefs() if(!client || !client.prefs_vr) - return 0 + return FALSE if(!client.prefs_vr.load_vore()) - return 0 + return FALSE if(!copy_from_prefs_vr()) - return 0 + return FALSE - return 1 + return TRUE /mob/living/proc/copy_to_prefs_vr() if(!client || !client.prefs_vr) to_chat(src,"You attempted to save your vore prefs but somehow you're in this character without a client.prefs_vr variable. Tell a dev.") - return 0 + return FALSE var/datum/vore_preferences/P = client.prefs_vr P.digestable = src.digestable + P.devourable = src.devourable + P.feeding = src.feeding P.digest_leave_remains = src.digest_leave_remains P.allowmobvore = src.allowmobvore P.vore_taste = src.vore_taste + P.permit_healbelly = src.permit_healbelly P.can_be_drop_prey = src.can_be_drop_prey P.can_be_drop_pred = src.can_be_drop_pred @@ -209,7 +232,7 @@ P.belly_prefs = serialized - return 1 + return TRUE // // Proc for applying vore preferences, given bellies @@ -217,14 +240,17 @@ /mob/living/proc/copy_from_prefs_vr() if(!client || !client.prefs_vr) to_chat(src,"You attempted to apply your vore prefs but somehow you're in this character without a client.prefs_vr variable. Tell a dev.") - return 0 + return FALSE var/datum/vore_preferences/P = client.prefs_vr digestable = P.digestable + devourable = P.devourable + feeding = P.feeding digest_leave_remains = P.digest_leave_remains allowmobvore = P.allowmobvore vore_taste = P.vore_taste + permit_healbelly = P.permit_healbelly can_be_drop_prey = P.can_be_drop_prey can_be_drop_pred = P.can_be_drop_pred @@ -233,7 +259,7 @@ for(var/entry in P.belly_prefs) list_to_object(entry,src) - return 1 + return TRUE // // Release everything in every vore organ @@ -301,7 +327,7 @@ /mob/living/proc/get_taste_message(allow_generic = 1) if(!vore_taste && !allow_generic) - return 0 + return FALSE var/taste_message = "" if(vore_taste && (vore_taste != "")) @@ -334,6 +360,7 @@ return //Actual escaping absorbed = 0 //Make sure we're not absorbed + muffled = 0 //Removes Muffling forceMove(get_turf(src)) //Just move me up to the turf, let's not cascade through bellies, there's been a problem, let's just leave. for(var/mob/living/simple_mob/SA in range(10)) SA.prey_excludes[src] = world.time @@ -406,7 +433,7 @@ var/user_to_prey = get_dist(get_turf(user),get_turf(prey)) if(user_to_pred > 1 || user_to_prey > 1) - return 0 + return FALSE // Prepare messages if(user == pred) //Feeding someone to yourself @@ -428,14 +455,15 @@ //Timer and progress bar if(!do_after(user, swallow_time, prey)) - return 0 // Prey escpaed (or user disabled) before timer expired. + return FALSE // Prey escpaed (or user disabled) before timer expired. // If we got this far, nom successful! Announce it! user.visible_message(success_msg) // Actually shove prey into the belly. belly.nom_mob(prey, user) - user.update_icons() + if(!ishuman(user)) + user.update_icons() // Flavor handling if(belly.can_taste && prey.get_taste_message(FALSE)) @@ -446,7 +474,7 @@ add_attack_logs(pred,prey,"Eaten via [belly.name]") else add_attack_logs(user,pred,"Forced to eat [key_name(prey)]") - return 1 + return TRUE // // Magical pred-air breathing for inside preds @@ -600,6 +628,8 @@ I.forceMove(vore_selected) updateVRPanel() + log_admin("VORE: [src] used Eat Trash to swallow [I].") + if(istype(I,/obj/item/device/flashlight/flare) || istype(I,/obj/item/weapon/flame/match) || istype(I,/obj/item/weapon/storage/box/matches)) to_chat(src, "You can taste the flavor of spicy cardboard.") else if(istype(I,/obj/item/device/flashlight/glowstick)) @@ -641,6 +671,10 @@ to_chat(src, "You can taste the flavor of garbage and leftovers. Delicious?") else to_chat(src, "You can taste the flavor of gluttonous waste of food.") + //TFF 10/7/19 - Add custom flavour for collars for trash can trait. + else if (istype(I,/obj/item/clothing/accessory/collar)) + visible_message("[src] demonstrates their voracious capabilities by swallowing [I] whole!") + to_chat(src, "You can taste the submissiveness in the wearer of [I]!") else to_chat(src, "You can taste the flavor of garbage. Delicious.") return @@ -667,11 +701,19 @@ if(!user) CRASH("display_voreprefs() was called without an associated user.") var/dispvoreprefs = "[src]'s vore preferences


      " + if(client && client.prefs) + if("CHAT_OOC" in client.prefs.preferences_disabled) + dispvoreprefs += "OOC DISABLED
      " + if("CHAT_LOOC" in client.prefs.preferences_disabled) + dispvoreprefs += "LOOC DISABLED
      " dispvoreprefs += "Digestable: [digestable ? "Enabled" : "Disabled"]
      " + dispvoreprefs += "Devourable: [devourable ? "Enabled" : "Disabled"]
      " + dispvoreprefs += "Feedable: [feeding ? "Enabled" : "Disabled"]
      " dispvoreprefs += "Leaves Remains: [digest_leave_remains ? "Enabled" : "Disabled"]
      " dispvoreprefs += "Mob Vore: [allowmobvore ? "Enabled" : "Disabled"]
      " - dispvoreprefs += "Drop-nom prey: [can_be_drop_prey ? "Enabled" : "Disabled"]
      " - dispvoreprefs += "Drop-nom pred: [can_be_drop_pred ? "Enabled" : "Disabled"]
      " + dispvoreprefs += "Healbelly permission: [permit_healbelly ? "Allowed" : "Disallowed"]
      " + dispvoreprefs += "Spontaneous vore prey: [can_be_drop_prey ? "Enabled" : "Disabled"]
      " + dispvoreprefs += "Spontaneous vore pred: [can_be_drop_pred ? "Enabled" : "Disabled"]
      " user << browse("Vore prefs: [src]
      [dispvoreprefs]
      ", "window=[name];size=200x300;can_resize=0;can_minimize=0") onclose(user, "[name]") return diff --git a/code/modules/vore/eating/simple_animal_vr.dm b/code/modules/vore/eating/simple_animal_vr.dm index d5eb653d99..11e5035099 100644 --- a/code/modules/vore/eating/simple_animal_vr.dm +++ b/code/modules/vore/eating/simple_animal_vr.dm @@ -1,6 +1,6 @@ ///////////////////// Simple Animal ///////////////////// /mob/living/simple_mob - var/swallowTime = 30 //How long it takes to eat its prey in 1/10 of a second. The default is 3 seconds. + var/swallowTime = (3 SECONDS) //How long it takes to eat its prey in 1/10 of a second. The default is 3 seconds. var/list/prey_excludes = list() //For excluding people from being eaten. // @@ -42,13 +42,24 @@ var/confirm = alert(user, "Enabling digestion on [name] will cause it to digest all stomach contents. Using this to break OOC prefs is against the rules. Digestion will reset after 20 minutes.", "Enabling [name]'s Digestion", "Enable", "Cancel") if(confirm == "Enable") vore_selected.digest_mode = DM_DIGEST - spawn(12000) //12000=20 minutes + spawn(20 MINUTES) if(src) vore_selected.digest_mode = vore_default_mode else var/confirm = alert(user, "This mob is currently set to process all stomach contents. Do you want to disable this?", "Disabling [name]'s Digestion", "Disable", "Cancel") if(confirm == "Disable") vore_selected.digest_mode = DM_HOLD +/mob/living/simple_mob/verb/toggle_fancygurgle() + set name = "Toggle Animal's Gurgle sounds" + set desc = "Switches between Fancy and Classic sounds on this mob." + set category = "OOC" + set src in oview(1) + + var/mob/living/user = usr //I mean, At least ghosts won't use it. + if(!istype(user) || user.stat) return + + vore_selected.fancy_vore = !vore_selected.fancy_vore + to_chat(user, "[src] is now using [vore_selected.fancy_vore ? "Fancy" : "Classic"] vore sounds.") /mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user) if (istype(O, /obj/item/weapon/newspaper) && !(ckey || (ai_holder.hostile && faction != user.faction)) && isturf(user.loc)) @@ -70,7 +81,7 @@ for(var/mob/living/L in living_mobs(0)) //add everyone on the tile to the do-not-eat list for a while if(!(L in prey_excludes)) // Unless they're already on it, just to avoid fuckery. prey_excludes += L - spawn(3600) + spawn(5 MINUTES) if(src && L) prey_excludes -= L else diff --git a/code/modules/vore/eating/vore_vr.dm b/code/modules/vore/eating/vore_vr.dm index f89462f23c..168ec9ccfa 100644 --- a/code/modules/vore/eating/vore_vr.dm +++ b/code/modules/vore/eating/vore_vr.dm @@ -19,12 +19,14 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE -Aro <3 */ +#define VORE_VERSION 2 //This is a Define so you don't have to worry about magic numbers. + // // Overrides/additions to stock defines go here, as well as hooks. Sort them by // the object they are overriding. So all /mob/living together, etc. // /datum/configuration - var/items_survive_digestion = 1 //For configuring if the important_items survive digestion + var/items_survive_digestion = TRUE //For configuring if the important_items survive digestion // // The datum type bolted onto normal preferences datums for storing Virgo stuff @@ -35,17 +37,20 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE /hook/client_new/proc/add_prefs_vr(client/C) C.prefs_vr = new/datum/vore_preferences(C) if(C.prefs_vr) - return 1 + return TRUE - return 0 + return FALSE /datum/vore_preferences //Actual preferences var/digestable = TRUE + var/devourable = TRUE + var/feeding = TRUE var/digest_leave_remains = FALSE var/allowmobvore = TRUE var/list/belly_prefs = list() var/vore_taste = "nothing in particular" + var/permit_healbelly = TRUE var/can_be_drop_prey = FALSE var/can_be_drop_pred = FALSE @@ -67,9 +72,9 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE /proc/is_vore_predator(var/mob/living/O) if(istype(O,/mob/living)) if(O.vore_organs.len > 0) - return 1 + return TRUE - return 0 + return FALSE // // Belly searching for simplifying other procs @@ -88,30 +93,33 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE /datum/vore_preferences/proc/load_vore() if(!client || !client_ckey) - return 0 //No client, how can we save? + return FALSE //No client, how can we save? if(!client.prefs || !client.prefs.default_slot) - return 0 //Need to know what character to load! + return FALSE //Need to know what character to load! slot = client.prefs.default_slot load_path(client_ckey,slot) - if(!path) return 0 //Path couldn't be set? + if(!path) return FALSE //Path couldn't be set? if(!fexists(path)) //Never saved before save_vore() //Make the file first - return 1 + return TRUE var/list/json_from_file = json_decode(file2text(path)) if(!json_from_file) - return 0 //My concern grows + return FALSE //My concern grows var/version = json_from_file["version"] json_from_file = patch_version(json_from_file,version) digestable = json_from_file["digestable"] + devourable = json_from_file["devourable"] + feeding = json_from_file["feeding"] digest_leave_remains = json_from_file["digest_leave_remains"] allowmobvore = json_from_file["allowmobvore"] vore_taste = json_from_file["vore_taste"] + permit_healbelly = json_from_file["permit_healbelly"] can_be_drop_prey = json_from_file["can_be_drop_prey"] can_be_drop_pred = json_from_file["can_be_drop_pred"] belly_prefs = json_from_file["belly_prefs"] @@ -119,10 +127,16 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE //Quick sanitize if(isnull(digestable)) digestable = TRUE + if(isnull(devourable)) + devourable = TRUE + if(isnull(feeding)) + feeding = TRUE if(isnull(digest_leave_remains)) digest_leave_remains = FALSE if(isnull(allowmobvore)) allowmobvore = TRUE + if(isnull(permit_healbelly)) + permit_healbelly = TRUE if(isnull(can_be_drop_prey)) allowmobvore = FALSE if(isnull(can_be_drop_pred)) @@ -130,18 +144,21 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE if(isnull(belly_prefs)) belly_prefs = list() - return 1 + return TRUE /datum/vore_preferences/proc/save_vore() - if(!path) return 0 + if(!path) return FALSE - var/version = 1 //For "good times" use in the future + var/version = VORE_VERSION //For "good times" use in the future var/list/settings_list = list( "version" = version, "digestable" = digestable, + "devourable" = devourable, + "feeding" = feeding, "digest_leave_remains" = digest_leave_remains, "allowmobvore" = allowmobvore, "vore_taste" = vore_taste, + "permit_healbelly" = permit_healbelly, "can_be_drop_prey" = can_be_drop_prey, "can_be_drop_pred" = can_be_drop_pred, "belly_prefs" = belly_prefs, @@ -151,7 +168,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE var/json_to_file = json_encode(settings_list) if(!json_to_file) log_debug("Saving: [path] failed jsonencode") - return 0 + return FALSE //Write it out #ifdef RUST_G @@ -164,9 +181,9 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE #endif if(!fexists(path)) log_debug("Saving: [path] failed file write") - return 0 + return FALSE - return 1 + return TRUE //Can do conversions here /datum/vore_preferences/proc/patch_version(var/list/json_from_file,var/version) diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index c5e4391ef7..c9a212f293 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -189,6 +189,13 @@ dat += "Name:" dat += " '[selected.name]'" + //Belly Type button + dat += "
      Is this belly fleshy:" + dat += "[selected.is_wet ? "Yes" : "No"]" + if(selected.is_wet) + dat += "
      Internal loop for prey?:" + dat += "[selected.wet_loop ? "Yes" : "No"]" + //Digest Mode Button dat += "
      Belly Mode:" var/mode = selected.digest_mode @@ -229,10 +236,18 @@ dat += "
      Flavor Text:" dat += " '[selected.desc]'" + //Belly Sound Fanciness + dat += "
      Use Fancy Sounds:" + dat += "[selected.fancy_vore ? "Yes" : "No"]" + //Belly sound - dat += "
      Set Vore Sound" + dat += "
      Vore Sound: [selected.vore_sound]" dat += "Test" + //Release sound + dat += "
      Release Sound: [selected.release_sound]" + dat += "Test" + //Belly messages dat += "
      Belly Messages" @@ -293,25 +308,48 @@ dat += "
      " switch(user.digestable) - if(1) - dat += "Toggle Digestable" - if(0) - dat += "Toggle Digestable" - + if(TRUE) + dat += "Toggle Digestable (Currently: ON)" + if(FALSE) + dat += "Toggle Digestable (Currently: OFF)" + switch(user.devourable) + if(TRUE) + dat += "Toggle Devourable (Currently: ON)" + if(FALSE) + dat += "Toggle Devourable (Currently: OFF)" + switch(user.feeding) + if(TRUE) + dat += "
      Toggle Feeding (Currently: ON)" + if(FALSE) + dat += "
      Toggle Feeding (Currently: OFF)" switch(user.digest_leave_remains) - if(1) - dat += "Toggle Leaving Remains" - if(0) - dat += "Toggle Leaving Remains" - + if(TRUE) + dat += "Toggle Leaving Remains (Currently: ON)" + if(FALSE) + dat += "Toggle Leaving Remains (Currently: OFF)" switch(user.allowmobvore) - if(1) - dat += "Toggle Mob Vore" - if(0) - dat += "Toggle Mob Vore" + if(TRUE) + dat += "
      Toggle Mob Vore (Currently: ON)" + if(FALSE) + dat += "
      Toggle Mob Vore (Currently: OFF)" + switch(user.permit_healbelly) + if(TRUE) + dat += "Toggle Healbelly Permission (Currently: ON)" + if(FALSE) + dat += "Toggle Healbelly Permission (Currently: OFF)" + + switch(user.can_be_drop_prey) + if(TRUE) + dat += "
      Toggle Prey Spontaneous Vore (Currently: ON)" + if(FALSE) + dat += "
      Toggle Prey Spontaneous Vore (Currently: OFF)" + + switch(user.can_be_drop_pred) + if(TRUE) + dat += "Toggle Pred Spontaneous Vore (Currently: ON)" + if(FALSE) + dat += "Toggle Pred Spontaneous Vore (Currently: OFF)" - dat += "
      Toggle Drop-nom Prey" //These two get their own, custom row, too. - dat += "Toggle Drop-nom Pred" dat += "
      Set Your Taste" dat += "Toggle Hunger Noises" @@ -336,7 +374,7 @@ if(href_list["show_int"]) show_interacts = !show_interacts - return 1 //Force update + return TRUE //Force update if(href_list["int_help"]) alert("These control how your belly responds to someone using 'resist' while inside you. The percent chance to trigger each is listed below, \ @@ -344,13 +382,13 @@ These only function as long as interactions are turned on in general. Keep in mind, the 'belly mode' interactions (digest/absorb) \ will affect all prey in that belly, if one resists and triggers digestion/absorption. If multiple trigger at the same time, \ only the first in the order of 'Escape > Transfer > Absorb > Digest' will occur.","Interactions Help") - return 0 //Force update + return FALSE //Force update if(href_list["outsidepick"]) var/atom/movable/tgt = locate(href_list["outsidepick"]) var/obj/belly/OB = locate(href_list["outsidebelly"]) if(!(tgt in OB)) //Aren't here anymore, need to update menu. - return 1 + return TRUE var/intent = "Examine" if(istype(tgt,/mob/living)) @@ -363,7 +401,7 @@ if("Help Out") //Help the inside-mob out if(user.stat || user.absorbed || M.absorbed) to_chat(user,"You can't do that in your state!") - return 1 + return TRUE to_chat(user,"You begin to push [M] to freedom!") to_chat(M,"[usr] begins to push you to freedom!") @@ -382,11 +420,11 @@ if("Devour") //Eat the inside mob if(user.absorbed || user.stat) to_chat(user,"You can't do that in your state!") - return 1 + return TRUE if(!user.vore_selected) to_chat(user,"Pick a belly on yourself first!") - return 1 + return TRUE var/obj/belly/TB = user.vore_selected to_chat(user,"You begin to [lowertext(TB.vore_verb)] [M] into your [lowertext(TB.name)]!") @@ -404,7 +442,7 @@ var/obj/item/T = tgt if(!(tgt in OB)) //Doesn't exist anymore, update. - return 1 + return TRUE intent = alert("What do you want to do to that?","Query","Examine","Use Hand") switch(intent) if("Examine") @@ -413,7 +451,7 @@ if("Use Hand") if(user.stat) to_chat(user,"You can't do that in your state!") - return 1 + return TRUE user.ClickOn(T) sleep(5) //Seems to exit too fast for the panel to update @@ -426,23 +464,23 @@ intent = alert("Eject all, Move all?","Query","Eject all","Cancel","Move all") switch(intent) if("Cancel") - return 0 + return FALSE if("Eject all") if(user.stat) to_chat(user,"You can't do that in your state!") - return 0 + return FALSE selected.release_all_contents() if("Move all") if(user.stat) to_chat(user,"You can't do that in your state!") - return 0 + return FALSE var/obj/belly/choice = input("Move all where?","Select Belly") as null|anything in user.vore_organs if(!choice) - return 0 + return FALSE for(var/atom/movable/tgt in selected) to_chat(tgt,"You're squished from [user]'s [lowertext(selected)] to their [lowertext(choice.name)]!") @@ -450,7 +488,7 @@ var/atom/movable/tgt = locate(href_list["insidepick"]) if(!(tgt in selected)) //Old menu, needs updating because they aren't really there. - return 1 //Forces update + return TRUE //Forces update intent = "Examine" intent = alert("Examine, Eject, Move? Examine if you want to leave this box.","Query","Examine","Eject","Move") switch(intent) @@ -460,25 +498,25 @@ if("Eject") if(user.stat) to_chat(user,"You can't do that in your state!") - return 0 + return FALSE selected.release_specific_contents(tgt) if("Move") if(user.stat) to_chat(user,"You can't do that in your state!") - return 0 + return FALSE var/obj/belly/choice = input("Move [tgt] where?","Select Belly") as null|anything in user.vore_organs if(!choice || !(tgt in selected)) - return 0 + return FALSE to_chat(tgt,"You're squished from [user]'s [lowertext(selected.name)] to their [lowertext(choice.name)]!") selected.transfer_contents(tgt, choice) if(href_list["newbelly"]) if(user.vore_organs.len >= BELLIES_MAX) - return 0 + return FALSE var/new_name = html_encode(input(usr,"New belly's name:","New Belly") as text|null) @@ -495,7 +533,7 @@ if(failure_msg) //Something went wrong. alert(user,failure_msg,"Error!") - return 0 + return FALSE var/obj/belly/NB = new(user) NB.name = new_name @@ -524,10 +562,16 @@ if(failure_msg) //Something went wrong. alert(user,failure_msg,"Error!") - return 0 + return FALSE selected.name = new_name + if(href_list["b_wetness"]) + selected.is_wet = !selected.is_wet + + if(href_list["b_wetloop"]) + selected.wet_loop = !selected.wet_loop + if(href_list["b_mode"]) var/list/menu_list = selected.digest_modes.Copy() if(istype(usr,/mob/living/carbon/human)) @@ -535,13 +579,13 @@ var/new_mode = input("Choose Mode (currently [selected.digest_mode])") as null|anything in menu_list if(!new_mode) - return 0 + return FALSE if(new_mode == DM_TRANSFORM) //Snowflek submenu var/list/tf_list = selected.transform_modes var/new_tf_mode = input("Choose TF Mode (currently [selected.tf_mode])") as null|anything in tf_list if(!new_tf_mode) - return 0 + return FALSE selected.tf_mode = new_tf_mode selected.digest_mode = new_mode @@ -551,7 +595,7 @@ var/list/menu_list = selected.mode_flag_list.Copy() var/toggle_addon = input("Toggle Addon") as null|anything in menu_list if(!toggle_addon) - return 0 + return FALSE selected.mode_flags ^= selected.mode_flag_list[toggle_addon] selected.items_preserved.Cut() //Re-evaltuate all items in belly on addon toggle @@ -560,7 +604,7 @@ var/new_mode = input("Choose Mode (currently [selected.item_digest_mode])") as null|anything in menu_list if(!new_mode) - return 0 + return FALSE selected.item_digest_mode = new_mode selected.items_preserved.Cut() //Re-evaltuate all items in belly on belly-mode change @@ -572,14 +616,14 @@ var/list/menu_list = contamination_flavors.Copy() var/new_flavor = input("Choose Contamination Flavor Text Type (currently [selected.contamination_flavor])") as null|anything in menu_list if(!new_flavor) - return 0 + return FALSE selected.contamination_flavor = new_flavor if(href_list["b_contamination_color"]) var/list/menu_list = contamination_colors.Copy() var/new_color = input("Choose Contamination Color (currently [selected.contamination_color])") as null|anything in menu_list if(!new_color) - return 0 + return FALSE selected.contamination_color = new_color selected.items_preserved.Cut() //To re-contaminate for new color @@ -590,10 +634,10 @@ new_desc = readd_quotes(new_desc) if(length(new_desc) > BELLIES_DESC_MAX) alert("Entered belly desc too long. [BELLIES_DESC_MAX] character limit.","Error") - return 0 + return FALSE selected.desc = new_desc else //Returned null - return 0 + return FALSE if(href_list["b_msgs"]) var/list/messages = list( @@ -648,21 +692,58 @@ if(length(new_verb) > BELLIES_NAME_MAX || length(new_verb) < BELLIES_NAME_MIN) alert("Entered verb length invalid (must be longer than [BELLIES_NAME_MIN], no longer than [BELLIES_NAME_MAX]).","Error") - return 0 + return FALSE selected.vore_verb = new_verb - if(href_list["b_sound"]) - var/choice = input(user,"Currently set to [selected.vore_sound]","Select Sound") as null|anything in vore_sounds + if(href_list["b_fancy_sound"]) + selected.fancy_vore = !selected.fancy_vore + selected.vore_sound = "Gulp" + selected.release_sound = "Splatter" + // defaults as to avoid potential bugs + + if(href_list["b_release"]) + var/choice + if(selected.fancy_vore) + choice = input(user,"Currently set to [selected.release_sound]","Select Sound") as null|anything in fancy_release_sounds + else + choice = input(user,"Currently set to [selected.release_sound]","Select Sound") as null|anything in classic_release_sounds + if(!choice) - return 0 + return FALSE + + selected.release_sound = choice + + if(href_list["b_releasesoundtest"]) + var/sound/releasetest + if(selected.fancy_vore) + releasetest = fancy_release_sounds[selected.release_sound] + else + releasetest = classic_release_sounds[selected.release_sound] + + if(releasetest) + SEND_SOUND(user, releasetest) + + if(href_list["b_sound"]) + var/choice + if(selected.fancy_vore) + choice = input(user,"Currently set to [selected.vore_sound]","Select Sound") as null|anything in fancy_vore_sounds + else + choice = input(user,"Currently set to [selected.vore_sound]","Select Sound") as null|anything in classic_vore_sounds + + if(!choice) + return FALSE selected.vore_sound = choice if(href_list["b_soundtest"]) - var/soundfile = vore_sounds[selected.vore_sound] - if(soundfile) - user << soundfile + var/sound/voretest + if(selected.fancy_vore) + voretest = fancy_vore_sounds[selected.vore_sound] + else + voretest = classic_vore_sounds[selected.vore_sound] + if(voretest) + SEND_SOUND(user, voretest) if(href_list["b_tastes"]) selected.can_taste = !selected.can_taste @@ -736,7 +817,7 @@ var/obj/belly/choice = input("Where do you want your [lowertext(selected.name)] to lead if prey resists?","Select Belly") as null|anything in (user.vore_organs + "None - Remove" - selected) if(!choice) //They cancelled, no changes - return 0 + return FALSE else if(choice == "None - Remove") selected.transferlocation = null else @@ -754,8 +835,8 @@ if(href_list["b_del"]) var/alert = alert("Are you sure you want to delete your [lowertext(selected.name)]?","Confirmation","Delete","Cancel") - if(!alert == "Delete") - return 0 + if(!(alert == "Delete")) + return FALSE var/failure_msg = "" @@ -776,7 +857,7 @@ if(failure_msg) alert(user,failure_msg,"Error!") - return 0 + return FALSE qdel(selected) selected = user.vore_organs[1] @@ -791,7 +872,7 @@ if(href_list["applyprefs"]) var/alert = alert("Are you sure you want to reload character slot preferences? This will remove your current vore organs and eject their contents.","Confirmation","Reload","Cancel") if(!alert == "Reload") - return 0 + return FALSE if(!user.apply_vore_prefs()) alert("ERROR: Virgo-specific preferences failed to apply!","Error") else @@ -800,29 +881,29 @@ if(href_list["setflavor"]) var/new_flavor = html_encode(input(usr,"What your character tastes like (40ch limit). This text will be printed to the pred after 'X tastes of...' so just put something like 'strawberries and cream':","Character Flavor",user.vore_taste) as text|null) if(!new_flavor) - return 0 + return FALSE new_flavor = readd_quotes(new_flavor) if(length(new_flavor) > FLAVOR_MAX) alert("Entered flavor/taste text too long. [FLAVOR_MAX] character limit.","Error!") - return 0 + return FALSE user.vore_taste = new_flavor if(href_list["toggle_dropnom_pred"]) - var/choice = alert(user, "You are currently [user.can_be_drop_pred ? " able to eat prey that fall from above or that you fall onto" : "not able to eat prey that fall from above or that you fall onto."]", "", "Be Pred", "Cancel", "Don't be Pred") + var/choice = alert(user, "This toggle is for spontaneous, environment related vore as a predator, including drop-noms, teleporters, etc. You are currently [user.can_be_drop_pred ? " able to eat prey that you encounter by environmental actions." : "avoiding eating prey encountered in the environment."]", "", "Be Pred", "Cancel", "Don't be Pred") switch(choice) if("Cancel") - return 0 + return FALSE if("Be Pred") user.can_be_drop_pred = TRUE if("Don't be Pred") user.can_be_drop_pred = FALSE if(href_list["toggle_dropnom_prey"]) - var/choice = alert(user, "You are currently [user.can_be_drop_prey ? "able to be eaten." : "not able to be eaten."]", "", "Be Prey", "Cancel", "Don't Be Prey") + var/choice = alert(user, "This toggle is for spontaneous, environment related vore as a prey, including drop-noms, teleporters, etc. You are currently [user.can_be_drop_prey ? "able to be eaten by environmental actions." : "not able to be eaten by environmental actions."]", "", "Be Prey", "Cancel", "Don't Be Prey") switch(choice) if("Cancel") - return 0 + return FALSE if("Be Prey") user.can_be_drop_prey = TRUE if("Don't Be Prey") @@ -832,7 +913,7 @@ var/choice = alert(user, "This button is for those who don't like being digested. It can make you undigestable. Messages admins when changed, so don't try to use it for mechanical benefit. Set it once and save it. Digesting you is currently: [user.digestable ? "Allowed" : "Prevented"]", "", "Allow Digestion", "Cancel", "Prevent Digestion") switch(choice) if("Cancel") - return 0 + return FALSE if("Allow Digestion") user.digestable = TRUE if("Prevent Digestion") @@ -843,11 +924,37 @@ if(user.client.prefs_vr) user.client.prefs_vr.digestable = user.digestable + if(href_list["toggleddevour"]) + var/choice = alert(user, "This button is to toggle your ability to be devoured by others. Devouring is currently: [user.devourable ? "Allowed" : "Prevented"]", "", "Be Devourable", "Cancel", "Prevent being Devoured") + switch(choice) + if("Cancel") + return FALSE + if("Be Devourable") + user.devourable = TRUE + if("Prevent being Devoured") + user.devourable = FALSE + + if(user.client.prefs_vr) + user.client.prefs_vr.devourable = user.devourable + + if(href_list["toggledfeed"]) + var/choice = alert(user, "This button is to toggle your ability to be fed to or by others vorishly. Force Feeding is currently: [user.feeding ? "Allowed" : "Prevented"]", "", "Allow Feeding", "Cancel", "Prevent Feeding") + switch(choice) + if("Cancel") + return FALSE + if("Allow Feeding") + user.feeding = TRUE + if("Prevent Feeding") + user.feeding = FALSE + + if(user.client.prefs_vr) + user.client.prefs_vr.feeding = user.feeding + if(href_list["toggledlm"]) var/choice = alert(user, "This button allows preds to have your remains be left in their belly after you are digested. This will only happen if pred sets their belly to do so. Remains consist of skeletal parts. Currently you are [user.digest_leave_remains? "" : "not"] leaving remains.", "", "Allow Post-digestion Remains", "Cancel", "Disallow Post-digestion Remains") switch(choice) if("Cancel") - return 0 + return FALSE if("Allow Post-digestion Remains") user.digest_leave_remains = TRUE if("Disallow Post-digestion Remains") @@ -860,7 +967,7 @@ var/choice = alert(user, "This button is for those who don't like being eaten by mobs. Messages admins when changed, so don't try to use it for mechanical benefit. Set it once and save it. Mobs are currently: [user.allowmobvore ? "Allowed to eat" : "Prevented from eating"] you.", "", "Allow Mob Predation", "Cancel", "Prevent Mob Predation") switch(choice) if("Cancel") - return 0 + return FALSE if("Allow Mob Predation") user.allowmobvore = TRUE if("Prevent Mob Predation") @@ -871,15 +978,28 @@ if(user.client.prefs_vr) user.client.prefs_vr.allowmobvore = user.allowmobvore + if(href_list["togglehealbelly"]) + var/choice = alert(user, "This button is for those who don't like healbelly used on them as a mechanic. It does not affect anything, but is displayed under mechanical prefs for ease of quick checks. You are currently: [user.allowmobvore ? "Okay" : "Not Okay"] with players using healbelly on you.", "", "Allow Healing Belly", "Cancel", "Disallow Healing Belly") + switch(choice) + if("Cancel") + return FALSE + if("Allow Healing Belly") + user.permit_healbelly = TRUE + if("Disallow Healing Belly") + user.permit_healbelly = FALSE + + if(user.client.prefs_vr) + user.client.prefs_vr.permit_healbelly = user.permit_healbelly + if(href_list["togglenoisy"]) var/choice = alert(user, "Toggle audible hunger noises. Currently: [user.noisy ? "Enabled" : "Disabled"]", "", "Enable audible hunger", "Cancel", "Disable audible hunger") switch(choice) if("Cancel") - return 0 + return FALSE if("Enable audible hunger") user.noisy = TRUE if("Disable audible hunger") user.noisy = FALSE //Refresh when interacted with, returning 1 makes vore_look.Topic update - return 1 + return TRUE diff --git a/code/modules/vore/fluffstuff/custom_boxes_vr.dm b/code/modules/vore/fluffstuff/custom_boxes_vr.dm index 64ada7cd6c..07e018f7a6 100644 --- a/code/modules/vore/fluffstuff/custom_boxes_vr.dm +++ b/code/modules/vore/fluffstuff/custom_boxes_vr.dm @@ -57,13 +57,11 @@ // bwoincognito:Tasald Corlethian /obj/item/weapon/storage/box/fluff/tasald name = "Tasald's Kit" - desc = "A kit containing Talsald's equipment." + desc = "A kit containing Tasald's equipment." has_items = list( /obj/item/clothing/suit/storage/det_suit/fluff/tasald, /obj/item/clothing/suit/storage/det_suit/fluff/tas_coat, /obj/item/clothing/under/det/fluff/tasald, - // /obj/item/clothing/accessory/permit/gun/fluff/tasald_corlethian, - // /obj/item/weapon/gun/projectile/revolver/mateba/fluff/tasald_corlethian, /obj/item/weapon/implanter/loyalty) //bwoincognito:Octavious Ward @@ -149,11 +147,7 @@ new /obj/item/clothing/accessory/holster/hip(src) new /obj/item/clothing/suit/storage/fluff/modernfedcoat(src) new /obj/item/clothing/head/caphat/formal/fedcover(src) - new /obj/item/weapon/card/id/centcom/station/fluff/joanbadge(src) -// new /obj/item/weapon/gun/energy/gun/fluff/dominator(src) new /obj/item/clothing/suit/armor/det_suit(src) -// new /obj/item/clothing/accessory/permit/gun/fluff/joanrisu(src) -// new /obj/item/weapon/sword/fluff/joanaria(src) new /obj/item/weapon/flame/lighter/zippo/fluff/joan(src) new /obj/item/clothing/under/rank/internalaffairs/fluff/joan(src) new /obj/item/clothing/head/helmet/space/fluff/joan(src) @@ -169,7 +163,6 @@ ..() new /obj/item/clothing/accessory/holster/hip(src) new /obj/item/clothing/suit/storage/fluff/fedcoat(src) -// new /obj/item/weapon/gun/energy/gun/fluff/dominator(src) new /obj/item/clothing/suit/armor/det_suit(src) new /obj/item/clothing/accessory/storage/black_vest(src) new /obj/item/weapon/material/knife/tacknife/combatknife/fluff/katarina(src) @@ -193,32 +186,6 @@ can_hold = list(/obj/item/clothing/under/swimsuit/) has_items = list(/obj/item/clothing/under/swimsuit/fluff/penelope) -// Aerowing:Sebastian Aji -/obj/item/weapon/storage/box/fluff/sebastian_aji - name = "Sebastian's Lumoco Arms P3 Box" - has_items = list( - /obj/item/weapon/gun/projectile/pistol, - /obj/item/ammo_magazine/m9mm/compact/flash, - /obj/item/ammo_magazine/m9mm/compact/flash, - /obj/item/clothing/accessory/permit/gun/fluff/sebastian_aji) - -/* -/obj/item/weapon/storage/box/fluff/briana_moore - name = "Briana's Derringer Box" - has_items = list( - /obj/item/weapon/gun/projectile/derringer/fluff/briana, - /obj/item/clothing/accessory/permit/gun/fluff/briana_moore) -*/ - -/* -//SilencedMP5A5:Serdykov Antoz -/obj/item/weapon/storage/box/fluff/serdykov_antoz - name = "Serdy's Weapon Box" - has_items = list( - /obj/item/clothing/accessory/permit/gun/fluff/silencedmp5a5, - /obj/item/weapon/gun/projectile/colt/fluff/serdy) -*/ - // JackNoir413: Mor Xaina /obj/item/weapon/storage/box/fluff/morxaina name = "Fashionable clothes set" diff --git a/code/modules/vore/fluffstuff/custom_clothes_vr.dm b/code/modules/vore/fluffstuff/custom_clothes_vr.dm index d8fcf7e5be..1ba3941bbe 100644 --- a/code/modules/vore/fluffstuff/custom_clothes_vr.dm +++ b/code/modules/vore/fluffstuff/custom_clothes_vr.dm @@ -693,9 +693,6 @@ icon_override = 'icons/vore/custom_clothes_vr.dmi' item_state = "rig-hos_mob" - //Slightly improved security voidsuit, which when made, was: - //armor = list(melee = 50, bullet = 25, laser = 25, energy = 5, bomb = 45, bio = 100, rad = 10) - armor = list("melee" = 60, "bullet" = 35, "laser" = 35, "energy" = 15, "bomb" = 50, "bio" = 100, "rad" = 10) species_restricted = null //HOS Hardsuit Helmet @@ -709,7 +706,6 @@ icon_override = 'icons/vore/custom_clothes_vr.dmi' item_state = "rig0-hos_mob" - armor = list("melee" = 60, "bullet" = 35, "laser" = 35, "energy" = 15, "bomb" = 50, "bio" = 100, "rad" = 10) species_restricted = null //adk09:Lethe @@ -1524,6 +1520,14 @@ Departamental Swimsuits, for general use icon_override = 'icons/vore/custom_clothes_vr.dmi' item_state = "gnshorts" +/obj/item/clothing/under/fluff/v_nanovest + name = "Varmacorp nanovest" + desc = "A nifty little vest optimized for nanite contact." + icon = 'icons/vore/custom_clothes_vr.dmi' + icon_state = "nanovest" + icon_override = 'icons/vore/custom_clothes_vr.dmi' + item_state = "nanovest" + //General use /obj/item/clothing/suit/storage/fluff/loincloth name = "Loincloth" @@ -1811,3 +1815,103 @@ Departamental Swimsuits, for general use body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS w_class = ITEMSIZE_NORMAL slot = ACCESSORY_SLOT_OVER + +//General definition for bracer items. No icons. +/obj/item/clothing/accessory/bracer + name = "bracer" + desc = "A bracer." + icon_state = null + item_state = null + icon_override = null + slot_flags = SLOT_GLOVES | SLOT_TIE + w_class = ITEMSIZE_SMALL + slot = ACCESSORY_SLOT_ARMBAND + +//AegisOA:Xander Bevin +//WanderingDeviant:S'thasha Tavakdavi +/obj/item/clothing/accessory/bracer/fluff/xander_sthasha + name = "Plasteel Bracer" + desc = "A sturdy arm-guard of polished plasteel that sports gold trimming, silver tribal-looping etchings, and a single cut diamond set into its side. Attached to one's forearm with a small, magnetic clasp." + icon = 'icons/vore/custom_items_vr.dmi' + icon_state = "bracer_xander_sthasha" + icon_override = 'icons/vore/custom_onmob_vr.dmi' + item_state = "bracer_xander_sthasha" + +/obj/item/clothing/accessory/bracer/fluff/xander_sthasha/digest_act(var/atom/movable/item_storage = null) + return FALSE + +/obj/item/clothing/accessory/bracer/fluff/xander_sthasha/gurgle_contaminate(var/atom/movable/item_storage = null) + return FALSE + +//Heroman3003:Lauren Zackson +/obj/item/clothing/accessory/collar/fluff/goldenstring + name = "golden string" + desc = "It appears to just be a length of gold-colored string attached to a simple plastic clasp, meant to be worn around the neck" + icon = 'icons/vore/custom_items_vr.dmi' + icon_override = 'icons/vore/custom_onmob_vr.dmi' + icon_state = "goldenstring" + item_state = "goldenstring" + w_class = ITEMSIZE_TINY + slot_flags = SLOT_TIE + +//Chaoko99: Aika Hisakawa +/obj/item/clothing/suit/fluff/blue_trimmed_coat + name = "blue-trimmed greatcoat" + desc = "A heavy, form-obscuring coat with gilded buttons and azure trim." + icon = 'icons/vore/custom_clothes_vr.dmi' + icon_state = "aika_coat" + + icon_override = 'icons/vore/custom_clothes_vr.dmi' + item_state = "aika_coat_mob" + flags_inv = HIDEJUMPSUIT | HIDETIE + + item_icons = list( + slot_l_hand_str = 'icons/vore/custom_clothes_vr.dmi', + slot_r_hand_str = 'icons/vore/custom_clothes_vr.dmi', + ) + item_state_slots = list(slot_r_hand_str = "aika_coat_mob_r", slot_l_hand_str = "aika_coat_mob_l") + +//Burrito Justice: Jayda Wilson +/obj/item/clothing/under/solgov/utility/sifguard/medical/fluff + desc = "The utility uniform of the Society of Universal Cartographers, made from biohazard resistant material. This is an older issuing of the uniform, with integrated department markings." + + icon = 'icons/vore/custom_clothes_vr.dmi' + icon_override = 'icons/vore/custom_clothes_vr.dmi' + + icon_state = "blackutility_med" + worn_state = "blackutility_med" + item_state = "blackutility_med" + + rolled_down = 0 + rolled_sleeves = 0 + starting_accessories = null + item_icons = list() + +//Vorrarkul: Melanie Farmer +/obj/item/clothing/under/fluff/slime_skeleton + name = "Melanie's Skeleton" + desc = "The skeleton of a promethean, still covered in residual slime. Upon closer inspection, they're not even real bones!" + + icon = 'icons/vore/custom_clothes_vr.dmi' + icon_override = 'icons/vore/custom_clothes_vr.dmi' + + icon_state = "melanie_skeleton" + item_state = "melanie_skeleton_mob" + + body_parts_covered = 0 + + species_restricted = list("exclude", SPECIES_TESHARI) + +/obj/item/clothing/under/fluff/slime_skeleton/mob_can_equip(M as mob, slot) + if(!..()) + return 0 + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(!(H.get_species() == SPECIES_PROMETHEAN)) //Only wearable by slimes, since species_restricted actually checks bodytype, not species + return 0 + + return 1 + +/obj/item/clothing/under/fluff/slime_skeleton/digest_act(var/atom/movable/item_storage = null) + return FALSE //Indigestible diff --git a/code/modules/vore/fluffstuff/custom_guns_vr.dm b/code/modules/vore/fluffstuff/custom_guns_vr.dm index 7a0b26d176..f99910a123 100644 --- a/code/modules/vore/fluffstuff/custom_guns_vr.dm +++ b/code/modules/vore/fluffstuff/custom_guns_vr.dm @@ -730,7 +730,7 @@ icon_state = "r357" ammo_type = /obj/item/ammo_casing/a44/rubber -//Expedition pistol +//Expedition Frontier Phaser /obj/item/weapon/gun/energy/frontier name = "frontier phaser" desc = "An extraordinarily rugged laser weapon, built to last and requiring effectively no maintenance. Includes a built-in crank charger for recharging away from civilization." @@ -746,6 +746,7 @@ unacidable = 1 var/recharging = 0 + var/phase_power = 75 projectile_type = /obj/item/projectile/beam firemodes = list( @@ -764,7 +765,7 @@ if(!do_after(user, 10, src)) break playsound(get_turf(src),'sound/items/change_drill.ogg',25,1) - if(power_supply.give(60) < 60) + if(power_supply.give(phase_power) < phase_power) break recharging = 0 @@ -835,13 +836,14 @@ return ..() -//Expeditionary Holdout Phaser +//Expeditionary Holdout Phaser Pistol /obj/item/weapon/gun/energy/frontier/locked/holdout name = "holdout frontier phaser" desc = "An minaturized weapon designed for the purpose of expeditionary support to defend themselves on the field. Includes a built-in crank charger for recharging away from civilization. This one has a safety interlock that prevents firing while in proximity to the facility." icon = 'icons/obj/gun_vr.dmi' icon_state = "holdoutkill" item_state = null + phase_power = 100 w_class = ITEMSIZE_SMALL charge_cost = 600 diff --git a/code/modules/vore/fluffstuff/custom_items_vr.dm b/code/modules/vore/fluffstuff/custom_items_vr.dm index b2c001ed3f..94f427abc3 100644 --- a/code/modules/vore/fluffstuff/custom_items_vr.dm +++ b/code/modules/vore/fluffstuff/custom_items_vr.dm @@ -151,7 +151,7 @@ icon_state = "pda-joan" //Vorrarkul:Lucina Dakarim -/obj/item/device/pda/heads/cmo/lucinapda +/obj/item/device/pda/heads/cmo/fluff/lucinapda icon = 'icons/vore/custom_items_vr.dmi' icon_state = "pda-lucina" @@ -1244,7 +1244,8 @@ w_class = ITEMSIZE_SMALL origin_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_ILLEGAL = 7) - var/obj/item/weapon/cell/device/weapon/power_source + var/cell_type = /obj/item/weapon/cell/device/weapon + var/obj/item/weapon/cell/power_source var/charge_cost = 800 // cell/device/weapon has 2400 var/list/beacons = list() @@ -1259,7 +1260,10 @@ /obj/item/device/perfect_tele/New() ..() flags |= NOBLUDGEON - power_source = new (src) + if(cell_type) + power_source = new cell_type(src) + else + power_source = new /obj/item/weapon/cell/device(src) spk = new(src) spk.set_up(5, 0, src) spk.attach(src) @@ -1276,7 +1280,7 @@ /obj/item/device/perfect_tele/update_icon() if(!power_source) icon_state = "[initial(icon_state)]_o" - else if(ready && power_source.check_charge(charge_cost)) + else if(ready && (power_source.check_charge(charge_cost) || power_source.fully_charged())) icon_state = "[initial(icon_state)]" else icon_state = "[initial(icon_state)]_w" @@ -1338,7 +1342,7 @@ return /obj/item/device/perfect_tele/attackby(obj/W, mob/user) - if(istype(W,/obj/item/weapon/cell/device/weapon) && !power_source) + if(istype(W,cell_type) && !power_source) power_source = W power_source.update_icon() //Why doesn't a cell do this already? :| user.unEquip(power_source) @@ -1367,7 +1371,7 @@ return FALSE //Check for charge - if(!power_source.check_charge(charge_cost)) + if((!power_source.check_charge(charge_cost)) && (!power_source.fully_charged())) to_chat(user,"\The [src] does not have enough power left!") return FALSE @@ -1398,10 +1402,13 @@ //No, you can't port to or from away missions. Stupidly complicated check. var/turf/uT = get_turf(user) var/turf/dT = get_turf(destination) + var/list/dat = list() + dat["z_level_detection"] = using_map.get_map_levels(uT.z) + if(!uT || !dT) return FALSE - if( (uT.z != dT.z) && ( (uT.z > max_default_z_level() ) || (dT.z > max_default_z_level()) ) ) + if( (uT.z != dT.z) && (!(dT.z in dat["z_level_detection"])) ) to_chat(user,"\The [src] can't teleport you that far!") return FALSE @@ -1562,14 +1569,59 @@ desc = "A more limited translocator with a single beacon, useful for some things, like setting the mining department on fire accidentally. Legal for use in the pursuit of NanoTrasen interests, namely mining and exploration." icon_state = "minitrans" beacons_left = 1 //Just one - charge_cost = 2400 //One per + cell_type = /obj/item/weapon/cell/device + origin_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5) +/* /obj/item/device/perfect_tele/one_beacon/teleport_checks(mob/living/target,mob/living/user) var/turf/T = get_turf(destination) if(T && user.z != T.z) to_chat(user,"\The [src] is too far away from the beacon. Try getting closer first!") return FALSE return ..() +*/ + +/obj/item/device/perfect_tele/admin + name = "alien translocator" + desc = "This strange device allows one to teleport people and objects across large distances." + + cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien + charge_cost = 400 + beacons_left = 6 + failure_chance = 0 //Percent + +/obj/item/device/perfect_tele/admin/teleport_checks(mob/living/target,mob/living/user) + //Uhhuh, need that power source + if(!power_source) + to_chat(user,"\The [src] has no power source!") + return FALSE + + //Check for charge + if((!power_source.check_charge(charge_cost)) && (!power_source.fully_charged())) + to_chat(user,"\The [src] does not have enough power left!") + return FALSE + + //Only mob/living need apply. + if(!istype(user) || !istype(target)) + return FALSE + + //No, you can't teleport buckled people. + if(target.buckled) + to_chat(user,"The target appears to be attached to something...") + return FALSE + + //No, you can't teleport if it's not ready yet. + if(!ready) + to_chat(user,"\The [src] is still recharging!") + return FALSE + + //No, you can't teleport if there's no destination. + if(!destination) + to_chat(user,"\The [src] doesn't have a current valid destination set!") + return FALSE + + //Seems okay to me! + return TRUE //InterroLouis: Ruda Lizden /obj/item/clothing/accessory/badge/holo/detective/ruda @@ -1998,4 +2050,32 @@ name = "gold-trimmed hypospray" desc = "A gold-trimmed MKII hypospray. The name 'Kenzie Houser' is engraved on the side." icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "kenziehypo" \ No newline at end of file + icon_state = "kenziehypo" + +//Semaun - Viktor Solothurn +/obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask/fluff/viktor + name = "flask of expensive alcohol" + desc = "A standard vacuum-flask filled with good and expensive drink." + +/obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask/fluff/viktor/Initialize() + ..() + reagents.add_reagent("pwine", 60) + +//RadiantAurora: Tiemli Kroto +/obj/item/clothing/glasses/welding/tiemgogs + name = "custom-fitted welding goggles" + desc = "A pair of thick, custom-fitted goggles with LEDs above the lenses. Ruggedly engraved below the lenses is the name 'Tiemli Kroto'." + + icon = 'icons/vore/custom_items_vr.dmi' + icon_state = "tiemgogs" + + icon_override = 'icons/vore/custom_clothes_vr.dmi' + icon_state = "tiemgogs" + +/obj/item/clothing/glasses/welding/tiemgogs/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = 0) + if(..()) + if(H.ckey != "radiantaurora") + to_chat(H, "These don't look like they were made to fit you...") + return 0 + else + return 1 diff --git a/code/modules/vore/fluffstuff/custom_permits_vr.dm b/code/modules/vore/fluffstuff/custom_permits_vr.dm index ac8c689094..4eebf38f51 100644 --- a/code/modules/vore/fluffstuff/custom_permits_vr.dm +++ b/code/modules/vore/fluffstuff/custom_permits_vr.dm @@ -6,72 +6,16 @@ to_chat(user, "You cannot reset the naming locks on [src]. It's issued by CentCom and totally tamper-proof!") return -// aerowing:Sebastian Aji -/obj/item/clothing/accessory/permit/gun/fluff/sebastian_aji - name = "Sebastian Aji's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on April 17th, 2563." - - -/* arokha:Aronai Kadigan -/obj/item/clothing/accessory/permit/gun/fluff/aronai_kadigan - name = "Aronai Kadigan's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on February 16th, 2562." -*/ - -/* -// bwoincognito:Tasald Corlethian -/obj/item/clothing/accessory/permit/gun/fluff/tasald_corlethian - name = "Tasald Ajax Corlethian's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on August 2nd, 2562." -*/ - -/* dhaeleena:Dhaeleena M'iar -/obj/item/clothing/accessory/permit/gun/fluff/dhaeleena_miar - name = "Dhaeleena M'iar's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on March 1st, 2562." -*/ - // jertheace:Jeremiah 'Ace' Acacius /obj/item/clothing/accessory/permit/gun/fluff/ace name = "Jeremiah Acacius's Sidearm Permit" desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on November 10th, 2563." -/* joanrisu:Joan Risu -/obj/item/clothing/accessory/permit/gun/fluff/joanrisu - name = "Joan Risu's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on April 4th, 2562." -*/ - -/* luminescent_ring:Briana Moore -/obj/item/clothing/accessory/permit/gun/fluff/briana_moore - name = "Briana Moore's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on April 24th, 2562." -*/ - // ValiTheWolf: Vakashi /obj/item/clothing/accessory/permit/gun/fluff/Vakashi name = "Vakashi's Sidearm Permit" desc = "A card indicating that the owner is allowed to carry a sidearm that uses less-than-lethal munitions. It is issued by CentCom, so it is valid until it expires on March 1st, 2564." -/* silenedmp5a5:Serdykov Antoz -/obj/item/clothing/accessory/permit/gun/fluff/silencedmp5a5 - name = "Serdykov Antoz's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm that uses less-than-lethal munitions. It is issued by CentCom, so it is valid until it expires on April 24th, 2562." -*/ - -/* -// pawoverlord:Sorrel Cavalet -/obj/item/clothing/accessory/permit/gun/fluff/sorrel_cavalet - name = "Sorrel Cavalet's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on September 26th, 2562." -*/ - -/* hzdonut:Jesse Soemmer -/obj/item/clothing/accessory/permit/gun/fluff/JesseSoemmer - name = "Jesse Soemmer's Sidearm Permit" - desc = "A card indicating that the owner is allowed to carry a sidearm. It is issued by CentCom, so it is valid until it expires on March 4, 2563." // Apparently revoked? -Ace -*/ - /* Legacy Permits // BEGIN - PROTOTYPE /obj/item/clothing/accessory/permit/gun/fluff diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/cell_loaded.dm b/code/modules/vore/fluffstuff/guns/cell_loaded/cell_loaded.dm new file mode 100644 index 0000000000..8e9a5181e6 --- /dev/null +++ b/code/modules/vore/fluffstuff/guns/cell_loaded/cell_loaded.dm @@ -0,0 +1,282 @@ +// The Gun // +/obj/item/weapon/gun/projectile/cell_loaded //this one can load both medical and security cells! for ERT/admin use. + name = "multipurpose cell-loaded revolver" + desc = "Variety is the spice of life! This weapon is a hybrid of the KHI-102b 'Nanotech Selectable-Fire Weapon' and the Vey-Med ML-3 'Medigun', dubbed the 'NSFW-ML3M'. \ + It can fire both harmful and healing cells with an internal nanite fabricator and energy weapon cell loader. Up to three combinations of \ + energy beams can be configured at once. Ammo not included." + catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) + + icon = 'icons/vore/custom_guns_vr.dmi' + icon_state = "nsfw" + + icon_override = 'icons/vore/custom_guns_vr.dmi' + item_state = "gun" + + caliber = "nsfw" + + origin_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 6, TECH_MAGNETS = 4) + + fire_sound = 'sound/weapons/Taser.ogg' + + load_method = MAGAZINE //Nyeh heh hehhh. + magazine_type = null + allowed_magazines = list(/obj/item/ammo_magazine/cell_mag) + handle_casings = HOLD_CASINGS //Don't eject batteries! + recoil = 0 + var/charge_left = 0 + var/max_charge = 0 + charge_sections = 5 + +/obj/item/weapon/gun/projectile/cell_loaded/consume_next_projectile() + if(chambered && ammo_magazine) + var/obj/item/ammo_casing/microbattery/batt = chambered + if(batt.shots_left) + return new chambered.projectile_type() + else + for(var/B in ammo_magazine.stored_ammo) + var/obj/item/ammo_casing/microbattery/other_batt = B + if(istype(other_batt,chambered.type) && other_batt.shots_left) + switch_to(other_batt) + return new chambered.projectile_type() + break + + return null + +/obj/item/weapon/gun/projectile/cell_loaded/proc/update_charge() + charge_left = 0 + max_charge = 0 + + if(!chambered) + return + + var/obj/item/ammo_casing/microbattery/batt = chambered + + charge_left = batt.shots_left + max_charge = initial(batt.shots_left) + if(ammo_magazine) //Crawl to find more + for(var/B in ammo_magazine.stored_ammo) + var/obj/item/ammo_casing/microbattery/bullet = B + if(istype(bullet,batt.type)) + charge_left += bullet.shots_left + max_charge += initial(bullet.shots_left) + +/obj/item/weapon/gun/projectile/cell_loaded/proc/switch_to(obj/item/ammo_casing/microbattery/new_batt) + if(ishuman(loc)) + if(chambered && new_batt.type == chambered.type) + to_chat(loc,"\The [src] is now using the next [new_batt.type_name] power cell.") + else + to_chat(loc,"\The [src] is now firing [new_batt.type_name].") + + chambered = new_batt + update_charge() + update_icon() + +/obj/item/weapon/gun/projectile/cell_loaded/attack_self(mob/user) + if(!chambered) + return + + var/list/stored_ammo = ammo_magazine.stored_ammo + + if(stored_ammo.len == 1) + return //silly you. + + //Find an ammotype that ISN'T the same, or exhaust the list and don't change. + var/our_slot = stored_ammo.Find(chambered) + + for(var/index in 1 to stored_ammo.len) + var/true_index = ((our_slot + index - 1) % stored_ammo.len) + 1 // Stupid ONE BASED lists! + var/obj/item/ammo_casing/microbattery/next_batt = stored_ammo[true_index] + if(chambered != next_batt && !istype(next_batt, chambered.type)) + switch_to(next_batt) + break +/* +/obj/item/weapon/gun/projectile/cell_loaded/special_check(mob/user) + if(!chambered) + return + + var/obj/item/ammo_casing/microbattery/batt = chambered + if(!batt.shots_left) + return FALSE + + return TRUE +*/ +/obj/item/weapon/gun/projectile/cell_loaded/load_ammo(var/obj/item/A, mob/user) + . = ..() + if(ammo_magazine && ammo_magazine.stored_ammo.len) + switch_to(ammo_magazine.stored_ammo[1]) + +/obj/item/weapon/gun/projectile/cell_loaded/unload_ammo(mob/user, var/allow_dump=1) + chambered = null + return ..() + +/obj/item/weapon/gun/projectile/cell_loaded/update_icon() + update_charge() + + cut_overlays() + if(!chambered) + return + + var/obj/item/ammo_casing/microbattery/batt = chambered + var/batt_color = batt.type_color //Used many times + + //Mode bar + var/image/mode_bar = image(icon, icon_state = "[initial(icon_state)]_type") + mode_bar.color = batt_color + add_overlay(mode_bar) + + //Barrel color + var/image/barrel_color = image(icon, icon_state = "[initial(icon_state)]_barrel") + barrel_color.alpha = 150 + barrel_color.color = batt_color + add_overlay(barrel_color) + + //Charge bar + var/ratio = CEILING(((charge_left / max_charge) * charge_sections), 1) + for(var/i = 0, i < ratio, i++) + var/image/charge_bar = image(icon, icon_state = "[initial(icon_state)]_charge") + charge_bar.pixel_x = i + charge_bar.color = batt_color + add_overlay(charge_bar) + + +// The Magazine // +/obj/item/ammo_magazine/cell_mag + name = "microbattery magazine" + desc = "A microbattery holder for a cell-based variable weapon." + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "cell_mag" + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 5, TECH_MAGNETS = 3) + caliber = "nsfw" + ammo_type = /obj/item/ammo_casing/microbattery + initial_ammo = 0 + max_ammo = 3 + var/x_offset = 5 //for update_icon() shenanigans- moved here so it can be adjusted for bigger mags + var/capname = "nsfw_mag" //as above + var/chargename = "nsfw_mag" //as above + mag_type = MAGAZINE + + var/list/modes = list() + +/obj/item/ammo_magazine/cell_mag/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/ammo_casing/microbattery)) + var/obj/item/ammo_casing/microbattery/B = W + if(!istype(B, ammo_type)) + to_chat(user, "[B] does not fit into [src].") + return + if(stored_ammo.len >= max_ammo) + to_chat(user, "[src] is full!") + return + user.remove_from_mob(B) + B.loc = src + stored_ammo.Add(B) + update_icon() + playsound(user.loc, 'sound/weapons/flipblade.ogg', 50, 1) + update_icon() + +/obj/item/ammo_magazine/cell_mag/update_icon() + cut_overlays() + if(!stored_ammo.len) + return //Why bother + + var/current = 0 + for(var/B in stored_ammo) + var/obj/item/ammo_casing/microbattery/batt = B + var/image/cap = image(icon, icon_state = "[capname]_cap") + cap.color = batt.type_color + cap.pixel_x = current * x_offset //Caps don't need a pixel_y offset + add_overlay(cap) + + if(batt.shots_left) + var/ratio = CEILING(((batt.shots_left / initial(batt.shots_left)) * 4), 1) //4 is how many lights we have a sprite for + var/image/charge = image(icon, icon_state = "[chargename]_charge-[ratio]") + charge.color = "#29EAF4" //Could use battery color but eh. + charge.pixel_x = current * x_offset + add_overlay(charge) + + current++ //Increment for offsets + +/obj/item/ammo_magazine/cell_mag/advanced + name = "advanced microbattery magazine" + desc = "A microbattery holder for a cell-based variable weapon. This one has much more cell capacity!" + max_ammo = 6 + x_offset = 3 + icon_state = "cell_mag_extended" + + +// The Casing // +/obj/item/ammo_casing/microbattery + name = "\'NSFW\' microbattery - UNKNOWN" + desc = "A miniature battery for an energy weapon." + catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "nsfw_batt" + slot_flags = SLOT_BELT | SLOT_EARS + throwforce = 1 + w_class = ITEMSIZE_TINY + var/shots_left = 4 + + leaves_residue = 0 + caliber = "nsfw" + var/type_color = null + var/type_name = null + projectile_type = /obj/item/projectile/beam + +/obj/item/ammo_casing/microbattery/Initialize() + . = ..() + pixel_x = rand(-10, 10) + pixel_y = rand(-10, 10) + update_icon() + +/obj/item/ammo_casing/microbattery/update_icon() + cut_overlays() + + var/image/ends = image(icon, icon_state = "[initial(icon_state)]_ends") + ends.color = type_color + add_overlay(ends) + +/obj/item/ammo_casing/microbattery/expend() + shots_left-- + + +// The Pack // +/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hybrid + name = "hybrid cell-loaded gun kit" + desc = "A storage case for a multi-purpose handgun. Variety hour!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hybrid/New() + ..() + new /obj/item/weapon/gun/projectile/cell_loaded(src) + new /obj/item/ammo_magazine/cell_mag/advanced(src) + new /obj/item/ammo_casing/microbattery/combat/stun(src) + new /obj/item/ammo_casing/microbattery/combat/stun(src) + new /obj/item/ammo_casing/microbattery/combat/stun(src) + new /obj/item/ammo_casing/microbattery/combat/net(src) + new /obj/item/ammo_casing/microbattery/combat/net(src) + new /obj/item/ammo_casing/microbattery/medical/brute3(src) + new /obj/item/ammo_casing/microbattery/medical/burn3(src) + new /obj/item/ammo_casing/microbattery/medical/stabilize2(src) + new /obj/item/ammo_casing/microbattery/medical/toxin3(src) + new /obj/item/ammo_casing/microbattery/medical/omni3(src) + +/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hybrid_combat + name = "military cell-loaded gun kit" + desc = "A storage case for a multi-purpose handgun. Variety hour!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hybrid_combat/New() + ..() + new /obj/item/weapon/gun/projectile/cell_loaded(src) + new /obj/item/ammo_magazine/cell_mag/advanced(src) + new /obj/item/ammo_casing/microbattery/combat/shotstun(src) + new /obj/item/ammo_casing/microbattery/combat/shotstun(src) + new /obj/item/ammo_casing/microbattery/combat/lethal(src) + new /obj/item/ammo_casing/microbattery/combat/lethal(src) + new /obj/item/ammo_casing/microbattery/combat/lethal(src) + new /obj/item/ammo_casing/microbattery/combat/ion(src) + new /obj/item/ammo_casing/microbattery/combat/xray(src) + new /obj/item/ammo_casing/microbattery/medical/stabilize2(src) + new /obj/item/ammo_casing/microbattery/medical/haste(src) + new /obj/item/ammo_casing/microbattery/medical/resist(src) \ No newline at end of file diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m.dm b/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m.dm new file mode 100644 index 0000000000..6dbf09204f --- /dev/null +++ b/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m.dm @@ -0,0 +1,72 @@ +// The Gun // +/obj/item/weapon/gun/projectile/cell_loaded/medical + name = "cell-loaded medigun" + desc = "The ML-3 'Medigun', or ML3M for short, is a powerful cell-based ranged healing device based on the KHI-102b NSFW. \ + It uses an internal nanite fabricator, powered and controlled by discrete cells, to deliver a variety of effects at range. Up to six combinations of \ + healing beams can be configured at once, depending on cartridge used. Ammo not included." + catalogue_data = list(/datum/category_item/catalogue/information/organization/vey_med) + + icon_state = "ml3m" + description_info = "This is a ranged healing device that uses interchangable nanite discharge cells in a magazine. Each cell is a different healing beam type, and up to three can be loaded in the magazine. Each battery usually provides four discharges of that beam type, and multiple from the same type may be loaded to increase the number of shots for that type." + description_fluff = "The Vey-Med ML-3 'Medigun' allows one to customize their loadout in the field, or before deploying, to allow emergency response personnel to deliver a variety of ranged healing options." + description_antag = "" + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 2, TECH_BIO = 5) + allowed_magazines = list(/obj/item/ammo_magazine/cell_mag/medical) + +/obj/item/weapon/gun/projectile/cell_loaded/medical/cmo + name = "advanced cell-loaded medigun" + desc = "This is a variation on the ML-3 'Medigun', a powerful cell-based ranged healing device based on the KHI-102b NSFW. \ + It has an extended sight for increased accuracy, and much more comfortable grip. Ammo not included." + + icon_state = "ml3m_cmo" + + +// The Magazine // +/obj/item/ammo_magazine/cell_mag/medical //medical + name = "nanite magazine" + desc = "A nanite fabrication magazine for the \'ML-3/M\'" + catalogue_data = list(/datum/category_item/catalogue/information/organization/vey_med) + description_info = "This magazine holds self-charging nanite fabricators to power the ML-3 'Medigun'. Up to three can be loaded at once, and each provides four shots of their respective healing type. Loading multiple of the same type will provide additional shots of that type. The batteries can be recharged in a normal recharger." + ammo_type = /obj/item/ammo_casing/microbattery/medical + icon_state = "ml3m_mag" + origin_tech = list(TECH_MATERIAL = 3, TECH_BIO = 3) + +/obj/item/ammo_magazine/cell_mag/medical/advanced + name = "advanced nanite magazine" + desc = "A nanite discharge cell for the \'ML-3/M\'. This one is a more advanced version which can hold six individual nanite discharge cells." + max_ammo = 6 + x_offset = 3 + icon_state = "ml3m_mag_extended" + origin_tech = list(TECH_MATERIAL = 5, TECH_BIO = 5) + + +// The Pack // +/obj/item/weapon/storage/secure/briefcase/ml3m_pack_med + name = "\improper ML-3 \'Medigun\' kit" + desc = "A storage case for a multi-purpose healing gun. Variety hour!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/storage/secure/briefcase/ml3m_pack_med/New() + ..() + new /obj/item/weapon/gun/projectile/cell_loaded/medical(src) + new /obj/item/ammo_magazine/cell_mag/medical(src) + new /obj/item/ammo_casing/microbattery/medical/brute(src) + new /obj/item/ammo_casing/microbattery/medical/burn(src) + new /obj/item/ammo_casing/microbattery/medical/stabilize(src) + +/obj/item/weapon/storage/secure/briefcase/ml3m_pack_cmo + name = "\improper Advanced ML-3 \'Medigun\' kit" + desc = "A storage case for a multi-purpose healing gun. Variety hour!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/storage/secure/briefcase/ml3m_pack_cmo/New() + ..() + new /obj/item/weapon/gun/projectile/cell_loaded/medical/cmo(src) + new /obj/item/ammo_magazine/cell_mag/medical(src) + new /obj/item/ammo_casing/microbattery/medical/brute(src) + new /obj/item/ammo_casing/microbattery/medical/burn(src) + new /obj/item/ammo_casing/microbattery/medical/stabilize(src) + new /obj/item/ammo_casing/microbattery/medical/toxin(src) + new /obj/item/ammo_casing/microbattery/medical/omni(src) \ No newline at end of file diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m_cells.dm b/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m_cells.dm new file mode 100644 index 0000000000..49f0a15e3f --- /dev/null +++ b/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m_cells.dm @@ -0,0 +1,340 @@ +// The Casing // +/obj/item/ammo_casing/microbattery/medical + name = "\'ML-3/M\' nanite cell - UNKNOWN" + desc = "A miniature nanite fabricator for a medigun." + catalogue_data = list(/datum/category_item/catalogue/information/organization/vey_med) + icon_state = "ml3m_batt" + origin_tech = list(TECH_BIO = 2, TECH_MATERIAL = 1, TECH_MAGNETS = 2) + +/obj/item/projectile/beam/medical_cell + name = "\improper healing beam" + icon_state = "medbeam" + nodamage = 1 + damage = 0 + check_armour = "laser" + light_color = "#80F5FF" + + combustion = FALSE + + muzzle_type = /obj/effect/projectile/muzzle/medigun + tracer_type = /obj/effect/projectile/tracer/medigun + impact_type = /obj/effect/projectile/impact/medigun + +/obj/item/projectile/beam/medical_cell/on_hit(var/mob/living/carbon/human/target) //what does it do when it hits someone? + return + +/obj/item/ammo_casing/microbattery/medical/brute + name = "\'ML-3/M\' nanite cell - BRUTE" + type_color = "#BF0000" + type_name = "BRUTE" + projectile_type = /obj/item/projectile/beam/medical_cell/brute + +/obj/item/projectile/beam/medical_cell/brute/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustBruteLoss(-5) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/burn + name = "\'ML-3/M\' nanite cell - BURN" + type_color = "#FF8000" + type_name = "BURN" + projectile_type = /obj/item/projectile/beam/medical_cell/burn + +/obj/item/projectile/beam/medical_cell/burn/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustFireLoss(-5) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/stabilize + name = "\'ML-3/M\' nanite cell - STABILIZE" //Disinfects all open wounds, cures oxy damage + type_color = "#0080FF" + type_name = "STABILIZE" + projectile_type = /obj/item/projectile/beam/medical_cell/stabilize + +/obj/item/projectile/beam/medical_cell/stabilize/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustOxyLoss(-30) + for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) + var/obj/item/organ/external/O = target.organs_by_name[name] + for (var/datum/wound/W in O.wounds) + if (W.internal) + continue + W.disinfect() + target.add_modifier(/datum/modifier/stabilize, 20 SECONDS) + else + return 1 + +/datum/modifier/stabilize + name = "stabilize" + desc = "Your injuries are stabilized and your pain abates!" + mob_overlay_state = "cyan_sparkles" + stacks = MODIFIER_STACK_EXTEND + pain_immunity = TRUE + bleeding_rate_percent = 0.1 //only a little + incoming_oxy_damage_percent = 0 + +/obj/item/ammo_casing/microbattery/medical/toxin + name = "\'ML-3/M\' nanite cell - TOXIN" + type_color = "#00A000" + type_name = "TOXIN" + projectile_type = /obj/item/projectile/beam/medical_cell/toxin + +/obj/item/projectile/beam/medical_cell/toxin/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustToxLoss(-5) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/omni + name = "\'ML-3/M\' nanite cell - OMNI" + type_color = "#8040FF" + type_name = "OMNI" + projectile_type = /obj/item/projectile/beam/medical_cell/omni + +/obj/item/projectile/beam/medical_cell/omni/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustBruteLoss(-2.5) + target.adjustFireLoss(-2.5) + target.adjustToxLoss(-2.5) + target.adjustOxyLoss(-10) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/antirad + name = "\'ML-3/M\' nanite cell - ANTIRAD" + type_color = "#008000" + type_name = "ANTIRAD" + projectile_type = /obj/item/projectile/beam/medical_cell/antirad + +/obj/item/projectile/beam/medical_cell/antirad/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustToxLoss(-2.5) + target.radiation = max(target.radiation - 150, 0) //same as 5 units of arithrazine, sans the brute damage + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/brute2 + name = "\'ML-3/M\' nanite cell - BRUTE-II" + type_color = "#BF0000" + type_name = "BRUTE-II" + projectile_type = /obj/item/projectile/beam/medical_cell/brute2 + +/obj/item/projectile/beam/medical_cell/brute2/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustBruteLoss(-10) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/burn2 + name = "\'ML-3/M\' nanite cell - BURN-II" + type_color = "#FF8000" + type_name = "BURN-II" + projectile_type = /obj/item/projectile/beam/medical_cell/burn2 + +/obj/item/projectile/beam/medical_cell/burn2/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustFireLoss(-10) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/stabilize2 + name = "\'ML-3/M\' nanite cell - STABILIZE-II" //Disinfects and bandages all open wounds, cures all oxy damage + type_color = "#0080FF" + type_name = "STABILIZE-II" + projectile_type = /obj/item/projectile/beam/medical_cell/stabilize2 + +/obj/item/projectile/beam/medical_cell/stabilize2/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustOxyLoss(-200) + for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) + var/obj/item/organ/external/O = target.organs_by_name[name] + for (var/datum/wound/W in O.wounds) + if(W.internal) + continue + if(O.is_bandaged() == FALSE) + W.bandage() + if(O.is_salved() == FALSE) + W.salve() + W.disinfect() + target.add_modifier(/datum/modifier/stabilize, 20 SECONDS) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/omni2 + name = "\'ML-3/M\' nanite cell - OMNI-II" + type_color = "#8040FF" + type_name = "OMNI-II" + projectile_type = /obj/item/projectile/beam/medical_cell/omni2 + +/obj/item/projectile/beam/medical_cell/omni2/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustBruteLoss(-5) + target.adjustFireLoss(-5) + target.adjustToxLoss(-5) + target.adjustOxyLoss(-30) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/toxin2 + name = "\'ML-3/M\' nanite cell - TOXIN-II" + type_color = "#00A000" + type_name = "TOXIN-II" + projectile_type = /obj/item/projectile/beam/medical_cell/toxin2 + +/obj/item/projectile/beam/medical_cell/toxin2/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustToxLoss(-20) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/haste + name = "\'ML-3/M\' nanite cell - HASTE" + type_color = "#FF3300" + type_name = "HASTE" + projectile_type = /obj/item/projectile/beam/medical_cell/haste + +/obj/item/projectile/beam/medical_cell/haste/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.add_modifier(/datum/modifier/ml3mhaste, 20 SECONDS) + else + return 1 + +/datum/modifier/ml3mhaste + name = "haste" + desc = "You can move much faster!" + mob_overlay_state = "haste" + stacks = MODIFIER_STACK_EXTEND + slowdown = -0.5 //a little faster! + evasion = 1.15 //and a little harder to hit! + +/obj/item/ammo_casing/microbattery/medical/resist + name = "\'ML-3/M\' nanite cell - RESIST" + type_color = "#555555" + type_name = "RESIST" + projectile_type = /obj/item/projectile/beam/medical_cell/resist + +/obj/item/projectile/beam/medical_cell/resist/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.add_modifier(/datum/modifier/resistance, 20 SECONDS) + else + return 1 + +/datum/modifier/resistance + name = "resistance" + desc = "You resist 15% of all incoming damage and stuns!" + mob_overlay_state = "repel_missiles" + stacks = MODIFIER_STACK_EXTEND + disable_duration_percent = 0.85 + incoming_damage_percent = 0.85 + +/obj/item/ammo_casing/microbattery/medical/corpse_mend + name = "\'ML-3/M\' nanite cell - CORPSE MEND" + type_color = "#669900" + type_name = "CORPSE MEND" + projectile_type = /obj/item/projectile/beam/medical_cell/corpse_mend + +/obj/item/projectile/beam/medical_cell/corpse_mend/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + if(target.stat == DEAD) + target.adjustBruteLoss(-50) + target.adjustFireLoss(-50) + target.adjustToxLoss(-50) + target.adjustOxyLoss(-200) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/brute3 + name = "\'ML-3/M\' nanite cell - BRUTE-III" + type_color = "#BF0000" + type_name = "BRUTE-III" + projectile_type = /obj/item/projectile/beam/medical_cell/brute3 + +/obj/item/projectile/beam/medical_cell/brute3/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustBruteLoss(-20) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/burn3 + name = "\'ML-3/M\' nanite cell - BURN-III" + type_color = "#FF8000" + type_name = "BURN-III" + projectile_type = /obj/item/projectile/beam/medical_cell/burn3 + +/obj/item/projectile/beam/medical_cell/burn3/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustFireLoss(-20) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/toxin3 + name = "\'ML-3/M\' nanite cell - TOXIN-III" + type_color = "#00A000" + type_name = "TOXIN-III" + projectile_type = /obj/item/projectile/beam/medical_cell/toxin3 + +/obj/item/projectile/beam/medical_cell/toxin3/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustToxLoss(-20) + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/omni3 + name = "\'ML-3/M\' nanite cell - OMNI-III" + type_color = "#8040FF" + type_name = "OMNI-III" + projectile_type = /obj/item/projectile/beam/medical_cell/omni3 + +/obj/item/projectile/beam/medical_cell/omni3/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.adjustBruteLoss(-10) + target.adjustFireLoss(-10) + target.adjustToxLoss(-10) + target.adjustOxyLoss(-60) + else + return 1 + +// Illegal cells! +/obj/item/ammo_casing/microbattery/medical/shrink + name = "\'ML-3/M\' nanite cell - SHRINK" + type_color = "#910ffc" + type_name = "SHRINK" + projectile_type = /obj/item/projectile/beam/medical_cell/shrink + +/obj/item/projectile/beam/medical_cell/shrink/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.resize(0.5) + target.show_message("The beam fires into your body, changing your size!") + target.updateicon() + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/grow + name = "\'ML-3/M\' nanite cell - GROW" + type_color = "#fc0fdc" + type_name = "GROW" + projectile_type = /obj/item/projectile/beam/medical_cell/grow + +/obj/item/projectile/beam/medical_cell/grow/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.resize(2.0) + target.show_message("The beam fires into your body, changing your size!") + target.updateicon() + else + return 1 + +/obj/item/ammo_casing/microbattery/medical/normalsize + name = "\'ML-3/M\' nanite cell - NORMALSIZE" + type_color = "#C70FEC" + type_name = "NORMALSIZE" + projectile_type = /obj/item/projectile/beam/medical_cell/normalsize + +/obj/item/projectile/beam/medical_cell/normalsize/on_hit(var/mob/living/carbon/human/target) + if(istype(target, /mob/living/carbon/human)) + target.resize(1) + target.show_message("The beam fires into your body, changing your size!") + target.updateicon() + else + return 1 \ No newline at end of file diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw.dm b/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw.dm new file mode 100644 index 0000000000..241e2e2c1d --- /dev/null +++ b/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw.dm @@ -0,0 +1,75 @@ +// The Gun // +/obj/item/weapon/gun/projectile/cell_loaded/combat + name = "cell-loaded revolver" + desc = "Variety is the spice of life! The KHI-102b 'Nanotech Selectable-Fire Weapon', or NSFW for short, is an unholy hybrid of an ammo-driven \ + energy weapon that allows the user to mix and match their own fire modes. Up to four combinations of \ + energy beams can be configured at once. Ammo not included." + catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) + + description_fluff = "The Kitsuhana 'Nanotech Selectable Fire Weapon' allows one to customize their loadout in the field, or before deploying, to achieve various results in a weapon they are already familiar with wielding." + allowed_magazines = list(/obj/item/ammo_magazine/cell_mag/combat) + +/obj/item/weapon/gun/projectile/cell_loaded/combat/prototype + name = "prototype cell-loaded revolver" + desc = "Variety is the spice of life! A prototype based on KHI-102b 'Nanotech Selectable-Fire Weapon', or NSFW for short, is an unholy hybrid of an ammo-driven \ + energy weapon that allows the user to mix and match their own fire modes. Up to two combinations of \ + energy beams can be configured at once. Ammo not included." + + description_info = "This gun is an energy weapon that uses interchangable microbatteries in a magazine. Each battery is a different beam type, and up to three can be loaded in the magazine. Each battery usually provides four discharges of that beam type, and multiple from the same type may be loaded to increase the number of shots for that type." + description_antag = "" + allowed_magazines = list(/obj/item/ammo_magazine/cell_mag/combat/prototype) + + origin_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 4, TECH_MAGNETS = 3) + + +// The Magazine // +/obj/item/ammo_magazine/cell_mag/combat + name = "microbattery magazine" + desc = "A microbattery holder for the \'NSFW\'" + icon_state = "nsfw_mag" + max_ammo = 4 + x_offset = 4 + catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) + description_info = "This magazine holds NSFW microbatteries to power the NSFW handgun. Up to three can be loaded at once, and each provides four shots of their respective energy type. Loading multiple of the same type will provide additional shots of that type. The batteries can be recharged in a normal recharger." + ammo_type = /obj/item/ammo_casing/microbattery/combat + +/obj/item/ammo_magazine/cell_mag/combat/prototype + name = "prototype microbattery magazine" + icon_state = "nsfw_mag_prototype" + max_ammo = 2 + x_offset = 6 + catalogue_data = null + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_MAGNETS = 2) + + +// The Pack // +/obj/item/weapon/storage/secure/briefcase/nsfw_pack + name = "\improper KHI-102b \'NSFW\' gun kit" + desc = "A storage case for a multi-purpose handgun. Variety hour!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/storage/secure/briefcase/nsfw_pack/New() + ..() + new /obj/item/weapon/gun/projectile/cell_loaded/combat(src) + new /obj/item/ammo_magazine/cell_mag/combat(src) + for(var/path in subtypesof(/obj/item/ammo_casing/microbattery/combat)) + new path(src) + +/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hos + name = "\improper KHI-102b \'NSFW\' gun kit" + desc = "A storage case for a multi-purpose handgun. Variety hour!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hos/New() + ..() + new /obj/item/weapon/gun/projectile/cell_loaded/combat(src) + new /obj/item/ammo_magazine/cell_mag/combat(src) + new /obj/item/ammo_casing/microbattery/combat/lethal(src) + new /obj/item/ammo_casing/microbattery/combat/lethal(src) + new /obj/item/ammo_casing/microbattery/combat/stun(src) + new /obj/item/ammo_casing/microbattery/combat/stun(src) + new /obj/item/ammo_casing/microbattery/combat/stun(src) + new /obj/item/ammo_casing/microbattery/combat/net(src) + new /obj/item/ammo_casing/microbattery/combat/ion(src) \ No newline at end of file diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw_cells.dm b/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw_cells.dm new file mode 100644 index 0000000000..946368baad --- /dev/null +++ b/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw_cells.dm @@ -0,0 +1,115 @@ +// The Casing // +/obj/item/ammo_casing/microbattery/combat + name = "\'NSFW\' microbattery - UNKNOWN" + desc = "A miniature battery for an energy weapon." + catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) + origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 1, TECH_MAGNETS = 2) + +/obj/item/ammo_casing/microbattery/combat/lethal + name = "\'NSFW\' microbattery - LETHAL" + type_color = "#bf3d3d" + type_name = "LETHAL" + projectile_type = /obj/item/projectile/beam + +/obj/item/ammo_casing/microbattery/combat/stun + name = "\'NSFW\' microbattery - STUN" + type_color = "#0f81bc" + type_name = "STUN" + projectile_type = /obj/item/projectile/beam/stun/blue + +/obj/item/ammo_casing/microbattery/combat/net + name = "\'NSFW\' microbattery - NET" + type_color = "#43f136" + type_name = "NET" + projectile_type = /obj/item/projectile/beam/energy_net + +/obj/item/ammo_casing/microbattery/combat/xray + name = "\'NSFW\' microbattery - XRAY" + type_color = "#32c025" + type_name = "XRAY" + projectile_type = /obj/item/projectile/beam/xray + +/obj/item/ammo_casing/microbattery/combat/shotstun + name = "\'NSFW\' microbattery - SCATTERSTUN" + type_color = "#88ffff" + type_name = "SCATTERSTUN" + projectile_type = /obj/item/projectile/bullet/pellet/e_shot_stun + +/obj/item/projectile/bullet/pellet/e_shot_stun + icon_state = "spell" + damage = 2 + agony = 20 + pellets = 6 //number of pellets + range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer. + base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone + spread_step = 10 + embed_chance = 0 + sharp = 0 + check_armour = "melee" + +/obj/item/ammo_casing/microbattery/combat/ion + name = "\'NSFW\' microbattery - ION" + type_color = "#d084d6" + type_name = "ION" + projectile_type = /obj/item/projectile/ion/small + +/obj/item/ammo_casing/microbattery/combat/stripper + name = "\'NSFW\' microbattery - STRIPPER" + type_color = "#fc8d0f" + type_name = "STRIPPER" + projectile_type = /obj/item/projectile/bullet/stripper + +/obj/item/projectile/bullet/stripper + icon_state = "magicm" + nodamage = 1 + agony = 5 + embed_chance = 0 + sharp = 0 + check_armour = "melee" + +/obj/item/projectile/bullet/stripper/on_hit(var/atom/stripped) + if(ishuman(stripped)) + var/mob/living/carbon/human/H = stripped + if(H.wear_suit) + H.unEquip(H.wear_suit) + if(H.w_uniform) + H.unEquip(H.w_uniform) + if(H.back) + H.unEquip(H.back) + if(H.shoes) + H.unEquip(H.shoes) + if(H.gloves) + H.unEquip(H.gloves) + //Hats can stay! Most other things fall off with removing these. + ..() + +/obj/item/ammo_casing/microbattery/combat/final + name = "\'NSFW\' microbattery - FINAL OPTION" + type_color = "#fcfc0f" + type_name = "FINAL OPTION" //Doesn't look good in yellow in chat + projectile_type = /obj/item/projectile/beam/final_option + +/obj/item/projectile/beam/final_option + name = "final option beam" + icon_state = "omnilaser" + nodamage = 1 + agony = 5 + damage_type = HALLOSS + light_color = "#00CC33" + + muzzle_type = /obj/effect/projectile/muzzle/laser_omni + tracer_type = /obj/effect/projectile/tracer/laser_omni + impact_type = /obj/effect/projectile/impact/laser_omni + +/obj/item/projectile/beam/final_option/on_hit(var/atom/impacted) + if(isliving(impacted)) + var/mob/living/L = impacted + if(L.mind) + var/nif + if(ishuman(L)) + var/mob/living/carbon/human/H = L + nif = H.nif + SStranscore.m_backup(L.mind,nif,one_time = TRUE) + L.gib() + + ..() \ No newline at end of file diff --git a/code/modules/vore/fluffstuff/guns/nsfw.dm b/code/modules/vore/fluffstuff/guns/nsfw.dm deleted file mode 100644 index 78aa05de53..0000000000 --- a/code/modules/vore/fluffstuff/guns/nsfw.dm +++ /dev/null @@ -1,370 +0,0 @@ -// -------------- NSFW ------------- -/obj/item/weapon/gun/projectile/nsfw - name = "cell-loaded revolver" - desc = "Variety is the spice of life! The KHI-102b 'Nanotech Selectable-Fire Weapon', or NSFW for short, is an unholy hybrid of an ammo-driven \ - energy weapon that allows the user to mix and match their own fire modes. Up to three combinations of \ - energy beams can be configured at once. Ammo not included." - catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) - - description_info = "This gun is an energy weapon that uses interchangable microbatteries in a magazine. Each battery is a different beam type, and up to three can be loaded in the magazine. Each battery usually provides four discharges of that beam type, and multiple from the same type may be loaded to increase the number of shots for that type." - description_fluff = "The Kitsuhana 'Nanotech Selectable Fire Weapon' allows one to customize their loadout in the field, or before deploying, to achieve various results in a weapon they are already familiar with wielding." - description_antag = "" - - icon = 'icons/vore/custom_guns_vr.dmi' - icon_state = "nsfw" - - icon_override = 'icons/vore/custom_guns_vr.dmi' - item_state = "gun" - - caliber = "nsfw" - - origin_tech = list(TECH_COMBAT = 7, TECH_MATERIAL = 6, TECH_MAGNETS = 4) - - fire_sound = 'sound/weapons/Taser.ogg' - - load_method = MAGAZINE //Nyeh heh hehhh. - magazine_type = null - allowed_magazines = list(/obj/item/ammo_magazine/nsfw_mag) - handle_casings = HOLD_CASINGS //Don't eject batteries! - recoil = 0 - var/charge_left = 0 - var/max_charge = 0 - charge_sections = 5 - -/obj/item/weapon/gun/projectile/nsfw/consume_next_projectile() - if(chambered && ammo_magazine) - var/obj/item/ammo_casing/nsfw_batt/batt = chambered - if(batt.shots_left) - return new chambered.projectile_type() - else - for(var/B in ammo_magazine.stored_ammo) - var/obj/item/ammo_casing/nsfw_batt/other_batt = B - if(istype(other_batt,chambered.type) && other_batt.shots_left) - switch_to(other_batt) - return new chambered.projectile_type() - break - - return null - -/obj/item/weapon/gun/projectile/nsfw/proc/update_charge() - charge_left = 0 - max_charge = 0 - - if(!chambered) - return - - var/obj/item/ammo_casing/nsfw_batt/batt = chambered - - charge_left = batt.shots_left - max_charge = initial(batt.shots_left) - if(ammo_magazine) //Crawl to find more - for(var/B in ammo_magazine.stored_ammo) - var/obj/item/ammo_casing/nsfw_batt/bullet = B - if(istype(bullet,batt.type)) - charge_left += bullet.shots_left - max_charge += initial(bullet.shots_left) - -/obj/item/weapon/gun/projectile/nsfw/proc/switch_to(obj/item/ammo_casing/nsfw_batt/new_batt) - if(ishuman(loc)) - if(chambered && new_batt.type == chambered.type) - to_chat(loc,"\The [src] is now using the next [new_batt.type_name] power cell.") - else - to_chat(loc,"\The [src] is now firing [new_batt.type_name].") - - chambered = new_batt - update_charge() - update_icon() - -/obj/item/weapon/gun/projectile/nsfw/attack_self(mob/user) - if(!chambered) - return - - var/list/stored_ammo = ammo_magazine.stored_ammo - - if(stored_ammo.len == 1) - return //silly you. - - //Find an ammotype that ISN'T the same, or exhaust the list and don't change. - var/our_slot = stored_ammo.Find(chambered) - - for(var/index in 1 to stored_ammo.len) - var/true_index = ((our_slot + index - 1) % stored_ammo.len) + 1 // Stupid ONE BASED lists! - var/obj/item/ammo_casing/nsfw_batt/next_batt = stored_ammo[true_index] - if(chambered != next_batt && !istype(next_batt, chambered.type)) - switch_to(next_batt) - break -/* -/obj/item/weapon/gun/projectile/nsfw/special_check(mob/user) - if(!chambered) - return - - var/obj/item/ammo_casing/nsfw_batt/batt = chambered - if(!batt.shots_left) - return FALSE - - return TRUE -*/ -/obj/item/weapon/gun/projectile/nsfw/load_ammo(var/obj/item/A, mob/user) - . = ..() - if(ammo_magazine && ammo_magazine.stored_ammo.len) - switch_to(ammo_magazine.stored_ammo[1]) - -/obj/item/weapon/gun/projectile/nsfw/unload_ammo(mob/user, var/allow_dump=1) - chambered = null - return ..() - -/obj/item/weapon/gun/projectile/nsfw/update_icon() - update_charge() - - cut_overlays() - if(!chambered) - return - - var/obj/item/ammo_casing/nsfw_batt/batt = chambered - var/batt_color = batt.type_color //Used many times - - //Mode bar - var/image/mode_bar = image(icon, icon_state = "[initial(icon_state)]_type") - mode_bar.color = batt_color - add_overlay(mode_bar) - - //Barrel color - var/image/barrel_color = image(icon, icon_state = "[initial(icon_state)]_barrel") - barrel_color.alpha = 150 - barrel_color.color = batt_color - add_overlay(barrel_color) - - //Charge bar - var/ratio = CEILING(((charge_left / max_charge) * charge_sections), 1) - for(var/i = 0, i < ratio, i++) - var/image/charge_bar = image(icon, icon_state = "[initial(icon_state)]_charge") - charge_bar.pixel_x = i - charge_bar.color = batt_color - add_overlay(charge_bar) - -// The Magazine // -/obj/item/ammo_magazine/nsfw_mag - name = "microbattery magazine" - desc = "A microbattery holder for the \'NSFW\'" - catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) - - description_info = "This magazine holds NSFW microbatteries to power the NSFW handgun. Up to three can be loaded at once, and each provides four shots of their respective energy type. Loading multiple of the same type will provide additional shots of that type. The batteries can be recharged in a normal recharger." - - icon = 'icons/obj/ammo_vr.dmi' - icon_state = "nsfw_mag" - caliber = "nsfw" - matter = list(DEFAULT_WALL_MATERIAL = 1680, "glass" = 2000) - ammo_type = /obj/item/ammo_casing/nsfw_batt - initial_ammo = 0 - max_ammo = 3 - mag_type = MAGAZINE - - var/list/modes = list() - -/obj/item/ammo_magazine/nsfw_mag/update_icon() - cut_overlays() - if(!stored_ammo.len) - return //Why bother - - var/x_offset = 5 - var/current = 0 - for(var/B in stored_ammo) - var/obj/item/ammo_casing/nsfw_batt/batt = B - var/image/cap = image(icon, icon_state = "[initial(icon_state)]_cap") - cap.color = batt.type_color - cap.pixel_x = current * x_offset //Caps don't need a pixel_y offset - add_overlay(cap) - - if(batt.shots_left) - var/ratio = CEILING(((batt.shots_left / initial(batt.shots_left)) * 4), 1) //4 is how many lights we have a sprite for - var/image/charge = image(icon, icon_state = "[initial(icon_state)]_charge-[ratio]") - charge.color = "#29EAF4" //Could use battery color but eh. - charge.pixel_x = current * x_offset - add_overlay(charge) - - current++ //Increment for offsets - -// The Casing // -/obj/item/ammo_casing/nsfw_batt - name = "\'NSFW\' microbattery - UNKNOWN" - desc = "A miniature battery for an energy weapon." - catalogue_data = list(/datum/category_item/catalogue/information/organization/khi) - icon = 'icons/obj/ammo_vr.dmi' - icon_state = "nsfw_batt" - slot_flags = SLOT_BELT | SLOT_EARS - throwforce = 1 - w_class = ITEMSIZE_TINY - - leaves_residue = 0 - caliber = "nsfw" - var/shots_left = 4 - var/type_color = null - var/type_name = null - projectile_type = /obj/item/projectile/beam - -/obj/item/ammo_casing/nsfw_batt/Initialize() - . = ..() - pixel_x = rand(-10, 10) - pixel_y = rand(-10, 10) - update_icon() - -/obj/item/ammo_casing/nsfw_batt/update_icon() - cut_overlays() - - var/image/ends = image(icon, icon_state = "[initial(icon_state)]_ends") - ends.color = type_color - add_overlay(ends) - -/obj/item/ammo_casing/nsfw_batt/expend() - shots_left-- - -// Specific batteries // -/obj/item/ammo_casing/nsfw_batt/lethal - name = "\'NSFW\' microbattery - LETHAL" - type_color = "#bf3d3d" - type_name = "LETHAL" - projectile_type = /obj/item/projectile/beam - -/obj/item/ammo_casing/nsfw_batt/stun - name = "\'NSFW\' microbattery - STUN" - type_color = "#0f81bc" - type_name = "STUN" - projectile_type = /obj/item/projectile/beam/stun/blue - -/obj/item/ammo_casing/nsfw_batt/net - name = "\'NSFW\' microbattery - NET" - type_color = "#43f136" - type_name = "NET" - projectile_type = /obj/item/projectile/beam/energy_net - -/obj/item/ammo_casing/nsfw_batt/xray - name = "\'NSFW\' microbattery - XRAY" - type_color = "#32c025" - type_name = "XRAY" - projectile_type = /obj/item/projectile/beam/xray - -/obj/item/ammo_casing/nsfw_batt/shotstun - name = "\'NSFW\' microbattery - SCATTERSTUN" - type_color = "#88ffff" - type_name = "SCATTERSTUN" - projectile_type = /obj/item/projectile/bullet/pellet/e_shot_stun - -/obj/item/projectile/bullet/pellet/e_shot_stun - icon_state = "spell" - damage = 2 - agony = 20 - pellets = 6 //number of pellets - range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer. - base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone - spread_step = 10 - embed_chance = 0 - sharp = 0 - check_armour = "melee" - -/obj/item/ammo_casing/nsfw_batt/ion - name = "\'NSFW\' microbattery - ION" - type_color = "#d084d6" - type_name = "ION" - projectile_type = /obj/item/projectile/ion/small - -/obj/item/ammo_casing/nsfw_batt/stripper - name = "\'NSFW\' microbattery - STRIPPER" - type_color = "#fc8d0f" - type_name = "STRIPPER" - projectile_type = /obj/item/projectile/bullet/stripper - -/obj/item/projectile/bullet/stripper - icon_state = "magicm" - nodamage = 1 - agony = 5 - embed_chance = 0 - sharp = 0 - check_armour = "melee" - -/obj/item/projectile/bullet/stripper/on_hit(var/atom/stripped) - if(ishuman(stripped)) - var/mob/living/carbon/human/H = stripped - if(H.wear_suit) - H.unEquip(H.wear_suit) - if(H.w_uniform) - H.unEquip(H.w_uniform) - if(H.back) - H.unEquip(H.back) - if(H.shoes) - H.unEquip(H.shoes) - if(H.gloves) - H.unEquip(H.gloves) - //Hats can stay! Most other things fall off with removing these. - ..() - -/obj/item/ammo_casing/nsfw_batt/final - name = "\'NSFW\' microbattery - FINAL OPTION" - type_color = "#fcfc0f" - type_name = "FINAL OPTION" //Doesn't look good in yellow in chat - projectile_type = /obj/item/projectile/beam/final_option - -/obj/item/projectile/beam/final_option - name = "final option beam" - icon_state = "omnilaser" - nodamage = 1 - agony = 5 - damage_type = HALLOSS - light_color = "#00CC33" - - muzzle_type = /obj/effect/projectile/muzzle/laser_omni - tracer_type = /obj/effect/projectile/tracer/laser_omni - impact_type = /obj/effect/projectile/impact/laser_omni - -/obj/item/projectile/beam/final_option/on_hit(var/atom/impacted) - if(isliving(impacted)) - var/mob/living/L = impacted - if(L.mind) - var/nif - if(ishuman(L)) - var/mob/living/carbon/human/H = L - nif = H.nif - SStranscore.m_backup(L.mind,nif,one_time = TRUE) - L.gib() - - ..() -/* -/obj/item/ammo_casing/nsfw_batt/shrink - name = "\'NSFW\' microbattery - SHRINK" - type_color = "#910ffc" - type_name = "SHRINK" - projectile_type = /obj/item/projectile/beam/shrinklaser - -/obj/item/ammo_casing/nsfw_batt/grow - name = "\'NSFW\' microbattery - GROW" - type_color = "#fc0fdc" - type_name = "GROW" - projectile_type = /obj/item/projectile/beam/growlaser -*/ -/obj/item/weapon/storage/secure/briefcase/nsfw_pack - name = "\improper KHI-102b \'NSFW\' gun kit" - desc = "A storage case for a multi-purpose handgun. Variety hour!" - max_w_class = ITEMSIZE_NORMAL - -/obj/item/weapon/storage/secure/briefcase/nsfw_pack/New() - ..() - new /obj/item/weapon/gun/projectile/nsfw(src) - new /obj/item/ammo_magazine/nsfw_mag(src) - for(var/path in subtypesof(/obj/item/ammo_casing/nsfw_batt)) - new path(src) - -/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hos - name = "\improper KHI-102b \'NSFW\' gun kit" - desc = "A storage case for a multi-purpose handgun. Variety hour!" - max_w_class = ITEMSIZE_NORMAL - -/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hos/New() - ..() - new /obj/item/weapon/gun/projectile/nsfw(src) - new /obj/item/ammo_magazine/nsfw_mag(src) - new /obj/item/ammo_casing/nsfw_batt/lethal(src) - new /obj/item/ammo_casing/nsfw_batt/lethal(src) - new /obj/item/ammo_casing/nsfw_batt/stun(src) - new /obj/item/ammo_casing/nsfw_batt/stun(src) - new /obj/item/ammo_casing/nsfw_batt/net(src) - new /obj/item/ammo_casing/nsfw_batt/ion(src) - diff --git a/code/modules/vore/fluffstuff/guns/protector.dm b/code/modules/vore/fluffstuff/guns/protector.dm index c66a8fb7d2..b090e963d6 100644 --- a/code/modules/vore/fluffstuff/guns/protector.dm +++ b/code/modules/vore/fluffstuff/guns/protector.dm @@ -108,12 +108,3 @@ muzzle_type = /obj/effect/projectile/muzzle/laser_omni tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni - -//R&D Design -/datum/design/item/weapon/protector - desc = "The 'Protector' is an advanced energy gun that cannot be fired in lethal mode on low security alert levels, but features DNA locking and a powerful stun." - id = "protector" - req_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) - materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "silver" = 1000) - build_path = /obj/item/weapon/gun/energy/protector - sort_string = "TAADA" diff --git a/code/modules/vore/fluffstuff/guns/pummeler.dm b/code/modules/vore/fluffstuff/guns/pummeler.dm index 082af9a9cf..75563208f2 100644 --- a/code/modules/vore/fluffstuff/guns/pummeler.dm +++ b/code/modules/vore/fluffstuff/guns/pummeler.dm @@ -46,12 +46,3 @@ L.throw_at(get_edge_target_turf(L, throwdir), rand(3,6), 10) return 1 - -//R&D Design -/datum/design/item/weapon/pummeler - desc = "With the 'Pummeler', punt anyone you don't like out of the room!" - id = "pummeler" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_MAGNET = 5) - materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 3000, "uranium" = 1000) - build_path = /obj/item/weapon/gun/energy/pummeler - sort_string = "TAADC" diff --git a/code/modules/vore/fluffstuff/guns/sickshot.dm b/code/modules/vore/fluffstuff/guns/sickshot.dm index 385a69efd6..098db69597 100644 --- a/code/modules/vore/fluffstuff/guns/sickshot.dm +++ b/code/modules/vore/fluffstuff/guns/sickshot.dm @@ -46,12 +46,3 @@ H.Confuse(2) return 1 - -//R&D Design -/datum/design/item/weapon/sickshot - desc = "A 'Sickshot' is a 4-shot energy revolver that causes nausea and confusion." - id = "sickshot" - req_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_MAGNET = 2) - materials = list(DEFAULT_WALL_MATERIAL = 3000, "glass" = 2000) - build_path = /obj/item/weapon/gun/energy/sickshot - sort_string = "TAADB" diff --git a/code/modules/vore/resizing/holder_micro_vr.dm b/code/modules/vore/resizing/holder_micro_vr.dm index 4e7e855ae8..d9db06118f 100644 --- a/code/modules/vore/resizing/holder_micro_vr.dm +++ b/code/modules/vore/resizing/holder_micro_vr.dm @@ -4,6 +4,7 @@ name = "micro" desc = "Another crewmember, small enough to fit in your hand." icon_state = "micro" + icon_override = 'icons/mob/head_vr.dmi' slot_flags = SLOT_FEET | SLOT_HEAD | SLOT_ID w_class = ITEMSIZE_SMALL item_icons = list() // No in-hand sprites (for now, anyway, we could totally add some) @@ -34,4 +35,9 @@ var/turf/here = get_turf(src) for(var/atom/movable/A in src) A.forceMove(here) - return ..() \ No newline at end of file + return ..() + +/obj/item/weapon/holder/micro/sync(var/mob/living/M) + ..() + for(var/mob/living/carbon/human/I in contents) + item_state = lowertext(I.species.name) \ No newline at end of file diff --git a/code/modules/vore/resizing/resize_vr.dm b/code/modules/vore/resizing/resize_vr.dm index c92121da91..2073f499c1 100644 --- a/code/modules/vore/resizing/resize_vr.dm +++ b/code/modules/vore/resizing/resize_vr.dm @@ -135,7 +135,7 @@ var/const/RESIZE_A_SMALLTINY = (RESIZE_SMALL + RESIZE_TINY) / 2 var/mob/living/simple_mob/SA = M if(!SA.has_hands) return 0 - if(M.buckled) + if(buckled) to_chat(usr,"You have to unbuckle \the [M] before you pick them up.") return 0 if(size_diff >= 0.50) diff --git a/code/modules/vore/resizing/sizegun_vr.dm b/code/modules/vore/resizing/sizegun_vr.dm index 705bf3538d..984e05f8ed 100644 --- a/code/modules/vore/resizing/sizegun_vr.dm +++ b/code/modules/vore/resizing/sizegun_vr.dm @@ -82,3 +82,11 @@ H.updateicon() else return 1 + + +/obj/item/projectile/beam/sizelaser/shrink + set_size = 0.5 //50% of current size + + +/obj/item/projectile/beam/sizelaser/grow + set_size = 2.0 //200% of current size \ No newline at end of file diff --git a/code/modules/xenoarcheaology/effects/radiate.dm b/code/modules/xenoarcheaology/effects/radiate.dm index a083cdddbc..e38540eb04 100644 --- a/code/modules/xenoarcheaology/effects/radiate.dm +++ b/code/modules/xenoarcheaology/effects/radiate.dm @@ -15,10 +15,10 @@ /datum/artifact_effect/radiate/DoEffectAura() if(holder) - radiation_repository.flat_radiate(holder, radiation_amount, src.effectrange) + SSradiation.flat_radiate(holder, radiation_amount, src.effectrange) return 1 /datum/artifact_effect/radiate/DoEffectPulse() if(holder) - radiation_repository.radiate(holder, ((radiation_amount * 3) * (sqrt(src.effectrange)))) //Need to get feedback on this //VOREStation Edit - Was too crazy-strong. + SSradiation.radiate(holder, ((radiation_amount * 3) * (sqrt(src.effectrange)))) //Need to get feedback on this //VOREStation Edit - Was too crazy-strong. return 1 diff --git a/code/modules/xenoarcheaology/finds/find_spawning.dm b/code/modules/xenoarcheaology/finds/find_spawning.dm index 37655b098f..be86279b08 100644 --- a/code/modules/xenoarcheaology/finds/find_spawning.dm +++ b/code/modules/xenoarcheaology/finds/find_spawning.dm @@ -355,8 +355,8 @@ //new_item = new /obj/item/weapon/fossil/base(src.loc) //the replacement item propogation isn't working, and it's messy code anyway so just do it here - var/list/candidates = list("/obj/item/weapon/fossil/bone"=9,"/obj/item/weapon/fossil/skull"=3, - "/obj/item/weapon/fossil/skull/horned"=2) + var/list/candidates = list(/obj/item/weapon/fossil/bone = 9,/obj/item/weapon/fossil/skull = 3, + /obj/item/weapon/fossil/skull/horned = 2) var/spawn_type = pickweight(candidates) new_item = new spawn_type(src.loc) @@ -511,6 +511,16 @@ desc = new_item.desc item_type = new_item.name + if(39) + // Telecube. + if(prob(25)) + apply_prefix = FALSE + if(prob(75)) + apply_image_decorations = TRUE + if(prob(25)) + apply_material_decorations = FALSE + new_item = new /obj/item/weapon/telecube/randomized(src.loc) + if(istype(new_item, /obj/item/weapon/material)) var/new_item_mat = pickweight( DEFAULT_WALL_MATERIAL = 80, @@ -605,6 +615,10 @@ new_item.origin_tech[TECH_ARCANE] = 1 new_item.origin_tech[TECH_PRECURSOR] = 1 + var/turf/simulated/mineral/T = get_turf(new_item) + if(istype(T)) + T.last_find = new_item + qdel(src) else if(talkative) diff --git a/code/modules/xenoarcheaology/finds/finds.dm b/code/modules/xenoarcheaology/finds/finds.dm index ab5d00e786..8e89dcd947 100644 --- a/code/modules/xenoarcheaology/finds/finds.dm +++ b/code/modules/xenoarcheaology/finds/finds.dm @@ -14,20 +14,22 @@ clearance_range = rand(4, 12) dissonance_spread = rand(1500, 2500) / 100 -/obj/item/weapon/ore/strangerock +/obj/item/weapon/strangerock name = "Strange rock" desc = "Seems to have some unusal strata evident throughout it." icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "strange" + var/datum/geosample/geologic_data origin_tech = list(TECH_MATERIAL = 5) -/obj/item/weapon/ore/strangerock/New(loc, var/inside_item_type = 0) - ..(loc) +/obj/item/weapon/strangerock/New(loc, var/inside_item_type = 0) + pixel_x = rand(0,16)-8 + pixel_y = rand(0,8)-8 if(inside_item_type) new /obj/item/weapon/archaeological_find(src, new_item_type = inside_item_type) -/obj/item/weapon/ore/strangerock/attackby(var/obj/item/I, var/mob/user) +/obj/item/weapon/strangerock/attackby(var/obj/item/I, var/mob/user) if(istype(I, /obj/item/weapon/pickaxe/brush)) var/obj/item/inside = locate() in src if(inside) diff --git a/code/modules/xenoarcheaology/finds/fossils.dm b/code/modules/xenoarcheaology/finds/fossils.dm index 8df5b92623..38e8d988f6 100644 --- a/code/modules/xenoarcheaology/finds/fossils.dm +++ b/code/modules/xenoarcheaology/finds/fossils.dm @@ -10,13 +10,10 @@ var/animal = 1 /obj/item/weapon/fossil/base/New() - var/list/l = list("/obj/item/weapon/fossil/bone"=9,"/obj/item/weapon/fossil/skull"=3, - "/obj/item/weapon/fossil/skull/horned"=2) + var/list/l = list(/obj/item/weapon/fossil/bone = 9,/obj/item/weapon/fossil/skull = 3, + /obj/item/weapon/fossil/skull/horned = 2) var/t = pickweight(l) - var/obj/item/weapon/W = new t(src.loc) - var/turf/T = get_turf(src) - if(istype(T, /turf/simulated/mineral)) - T:last_find = W + new t(src.loc) qdel(src) /obj/item/weapon/fossil/bone diff --git a/code/modules/xenoarcheaology/tools/coolant_tank.dm b/code/modules/xenoarcheaology/tools/coolant_tank.dm index df6c901517..9f5172b3de 100644 --- a/code/modules/xenoarcheaology/tools/coolant_tank.dm +++ b/code/modules/xenoarcheaology/tools/coolant_tank.dm @@ -11,7 +11,7 @@ /obj/structure/reagent_dispensers/coolanttank/bullet_act(var/obj/item/projectile/Proj) if(Proj.get_structure_damage()) - if(!istype(Proj ,/obj/item/projectile/beam/lastertag) && !istype(Proj ,/obj/item/projectile/beam/practice) ) // TODO: make this not terrible + if(!istype(Proj ,/obj/item/projectile/beam/lasertag) && !istype(Proj ,/obj/item/projectile/beam/practice) ) // TODO: make this not terrible explode() /obj/structure/reagent_dispensers/coolanttank/ex_act() diff --git a/code/modules/xenoarcheaology/tools/equipment.dm b/code/modules/xenoarcheaology/tools/equipment.dm index a2f21199a4..b241f714be 100644 --- a/code/modules/xenoarcheaology/tools/equipment.dm +++ b/code/modules/xenoarcheaology/tools/equipment.dm @@ -24,7 +24,7 @@ icon_state = "cespace_suit" item_state = "cespace_suit" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100) - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/pickaxe) slowdown = 1 // Pressure protection inherited from space suits diff --git a/code/modules/xenoarcheaology/tools/geosample_scanner.dm b/code/modules/xenoarcheaology/tools/geosample_scanner.dm index b97a34e48f..5b31f88400 100644 --- a/code/modules/xenoarcheaology/tools/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/tools/geosample_scanner.dm @@ -198,7 +198,7 @@ radiation = rand() * 15 + 85 if(!rad_shield) //irradiate nearby mobs - radiation_repository.radiate(src, radiation / 25) + SSradiation.radiate(src, radiation / 25) else t_left_radspike = pick(10,15,25) @@ -244,6 +244,7 @@ scanner_temperature = max(scanner_temperature - 5 - 10 * rand(), 0) if(prob(0.75)) src.visible_message("\icon[src] [pick("plinks","hisses")][pick(" quietly"," softly"," sadly"," plaintively")].", 2) + playsound(loc, 'sound/effects/ding.ogg', 25) last_process_worldtime = world.time /obj/machinery/radiocarbon_spectrometer/proc/stop_scanning() diff --git a/code/modules/xenoarcheaology/tools/tools_pickaxe.dm b/code/modules/xenoarcheaology/tools/tools_pickaxe.dm index d911843cca..778a204387 100644 --- a/code/modules/xenoarcheaology/tools/tools_pickaxe.dm +++ b/code/modules/xenoarcheaology/tools/tools_pickaxe.dm @@ -5,6 +5,8 @@ item_state = "syringe_0" slot_flags = SLOT_EARS digspeed = 20 + force = 0 + throwforce = 0 desc = "Thick metallic wires for clearing away dust and loose scree (1 centimetre excavation depth)." excavation_amount = 1 drill_sound = 'sound/weapons/thudswoosh.ogg' @@ -16,6 +18,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick1" item_state = "syringe_0" + force = 2 digspeed = 20 desc = "A miniature excavation tool for precise digging (2 centimetre excavation depth)." excavation_amount = 2 @@ -28,6 +31,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick2" item_state = "syringe_0" + force = 2 digspeed = 20 desc = "A miniature excavation tool for precise digging (4 centimetre excavation depth)." excavation_amount = 4 @@ -40,6 +44,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick3" item_state = "syringe_0" + force = 3 digspeed = 20 desc = "A miniature excavation tool for precise digging (6 centimetre excavation depth)." excavation_amount = 6 @@ -52,6 +57,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick4" item_state = "syringe_0" + force = 3 digspeed = 20 desc = "A miniature excavation tool for precise digging (8 centimetre excavation depth)." excavation_amount = 8 @@ -64,6 +70,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick5" item_state = "syringe_0" + force = 5 digspeed = 20 desc = "A miniature excavation tool for precise digging (10 centimetre excavation depth)." excavation_amount = 10 @@ -76,6 +83,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick6" item_state = "syringe_0" + force = 5 digspeed = 20 desc = "A miniature excavation tool for precise digging (12 centimetre excavation depth)." excavation_amount = 12 @@ -88,6 +96,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "pick_hand" item_state = "syringe_0" + force = 10 digspeed = 30 desc = "A smaller, more precise version of the pickaxe (30 centimetre excavation depth)." excavation_amount = 30 diff --git a/code/modules/xenobio/items/weapons.dm b/code/modules/xenobio/items/weapons.dm index 4abb89ffea..b44c73a032 100644 --- a/code/modules/xenobio/items/weapons.dm +++ b/code/modules/xenobio/items/weapons.dm @@ -37,17 +37,7 @@ // Research borg's version /obj/item/weapon/melee/baton/slime/robot hitcost = 200 - -/obj/item/weapon/melee/baton/slime/robot/attack_self(mob/user) - //try to find our power cell - var/mob/living/silicon/robot/R = loc - if (istype(R)) - bcell = R.cell - return ..() - -/obj/item/weapon/melee/baton/slime/robot/attackby(obj/item/weapon/W, mob/user) - return - + use_external_power = TRUE // Xeno stun gun + projectile /obj/item/weapon/gun/energy/taser/xeno diff --git a/config/alienwhitelist.txt b/config/alienwhitelist.txt index 4c69df3c3a..f7de728072 100644 --- a/config/alienwhitelist.txt +++ b/config/alienwhitelist.txt @@ -1,7 +1,9 @@ some~user - Species +911earlyarther - Xenomorph Hybrid admiraldragon - Vox -aether_elemental - Daemon +aetherelemental - Daemon +arandomalien - Xenochimera arokha - Protean aruis - Diona aruis - Xenochimera @@ -9,14 +11,18 @@ azmodan412 - Xenochimera azmodan412 - Xenomorph Hybrid bothnevarbackwards - Diona cameron653 - Xenomorph Hybrid +crossexonar - Protean funnyman2003 - Xenochimera hawkerthegreat - Vox +hollifex - Diona inuzari - Diona jademanique - Xenochimera jemli - Gutter +killerdragn - Xenomorph Hybrid ktccd - Diona mewchild - Diona mewchild - Vox +mrsebbi - Xenochimera natje - Xenochimera oreganovulgaris - Xenochimera paradoxspace - Xenochimera @@ -27,16 +33,19 @@ rikaru19xjenkins - Xenomorph Hybrid rikaru19xjenkins - Xenochimera rixunie - Diona rixunie - Gutter +rykkastormheart - Xenochimera seiga - Vox sepulchre - Vox sepulchre - Xenomorph Hybrid -silverTalismen - Diona -silverTalismen - Vox +silvertalismen - Diona +silvertalismen - Vox silvertalismen - Xenochimera singo - Gutter tastypred - Xenochimera +timidvi - Diona varonis - Xenochimera verkister - Xenochimera +westfire - Xenomorph Hybrid wickedtemp - Shadekin Empathy wtfismyname - Xenomorph Hybrid xioen - Diona diff --git a/config/custom_items.txt b/config/custom_items.txt index 3b363c070b..16734abd70 100644 --- a/config/custom_items.txt +++ b/config/custom_items.txt @@ -60,6 +60,12 @@ character_name: Lethe item_path: /obj/item/clothing/head/helmet/hos/fluff/lethe } +{ +ckey: aegisoa +character_name: Xander Bevin +item_path: /obj/item/clothing/accessory/bracer/fluff/xander_sthasha +} + { ckey: argobargsoup character_name: Lynn Shady @@ -69,7 +75,7 @@ item_path: /obj/item/device/flashlight/pen/fluff/lynn { ckey: arokha character_name: Aronai Kadigan -item_path: /obj/item/clothing/under/utility/sifguard/medical/command +item_path: /obj/item/clothing/under/solgov/utility/sifguard/medical/command item_name: centcom medical uniform item_desc: A medical uniform straight from Central Command. } @@ -88,13 +94,6 @@ character_name: Aronai Kadigan item_path: /obj/item/weapon/card/id/centcom/station/fluff/aronai } -Weapon Box. Permit Expired -{ -ckey: aerowing -character_name: Sebastian Aji -item_path: /obj/item/weapon/storage/box/fluff/sebastian_aji -} - # ######## B CKEYS { ckey: benemuel @@ -149,7 +148,7 @@ item_path: /obj/item/weapon/storage/box/fluff/octavious { ckey: burritojustice character_name: Jayda Wilson -item_path: /obj/item/clothing/under/utility/sifguard/medical +item_path: /obj/item/clothing/under/solgov/utility/sifguard/medical/fluff } # ######## C CKEYS @@ -190,31 +189,12 @@ item_path: /obj/item/weapon/disk/limb/eggnerdltd } # ######## D CKEYS -{ -ckey: deepindigo -character_name: Amina Dae-Kouri -item_path: /obj/item/weapon/storage/bible/fluff/amina -} - { ckey: dhaeleena character_name: Dhaeleena M'iar item_path: /obj/item/clothing/accessory/medal/silver/security/fluff/dhael } -#Permit Expired -#{ -#ckey: dhaeleena -#character_name: Dhaeleena M'iar -#item_path: /obj/item/weapon/gun/projectile/revolver/mateba/fluff/dhael -#} -# -#{ -#ckey: dhaeleena -#character_name: Dhaeleena M'iar -#item_path: /obj/item/clothing/accessory/permit/gun/fluff/dhaeleena_miar -#} - { ckey: dickfreedomjohnson character_name: Elliot Richards @@ -248,20 +228,6 @@ item_path: /obj/item/weapon/storage/box/fluff/yonra } # ######## E CKEYS - -# Permit Expired -#{ -#ckey: eekasqueak -#character_name: Serkii Miishy -#item_path: /obj/item/clothing/accessory/permit/gun/fluff/serkiimiishy -#} -# -#{ -#ckey: eekasqueak -#character_name: Serkii Miishy -#item_path: /obj/item/weapon/gun/energy/stunrevolver -#} - { ckey: eekasqueak character_name: Serkii Miishy @@ -287,8 +253,8 @@ item_path: /obj/item/clothing/accessory/medal/bronze_heart } # ######## F CKEYS -# ######## G CKEYS +# ######## G CKEYS { ckey:Gowst character_name: Eldi Moljir @@ -296,6 +262,17 @@ item_path: /obj/item/weapon/implanter/reagent_generator/eldi } # ######## H CKEYS +{ +ckey: heroman3003 +character_name: Lauren Zackson +item_path: /obj/item/clothing/accessory/medal/conduct +} + +{ +ckey: heroman3003 +character_name: Lauren Zackson +item_path: /obj/item/clothing/accessory/collar/fluff/goldenstring +} { ckey: hottokeeki @@ -315,26 +292,7 @@ character_name: Belle Day item_path: /obj/item/weapon/implanter/reagent_generator/belle } -#{ -#ckey: hzdonut -#character_name: Jesse Soemmer -#item_path: /obj/item/weapon/gun/projectile/revolver/fluff/jesselemat -#} -# -#{ -#ckey: hzdonut -#character_name: Jesse Soemmer -#item_path: /obj/item/clothing/accessory/permit/gun/fluff/JesseSoemmer -#} -# -#{ -#ckey: hzdonut -#character_name: Jesse Soemmer -#item_path: /obj/item/clothing/accessory/holster/hip -#} - # ######## I CKEYS - { ckey: interrolouis character_name: Ruda Lizden @@ -354,7 +312,6 @@ item_path: /obj/item/weapon/storage/backpack/messenger/sec/fluff/ivymoomoo } # ######## J CKEYS - { ckey: jacknoir413 character_name: Mor Xaina @@ -493,12 +450,6 @@ item_path: /obj/item/weapon/storage/box/fluff/koyoakimomi } # ######## K CKEYS -{ -ckey: kazkin -character_name: Ahzrukhal Ahkeen -item_path: /obj/item/clothing/accessory/medal/silver/unity -} - { ckey: keekenox character_name: SMU-453 @@ -541,12 +492,6 @@ character_name: Chakat Taiga item_path: /obj/item/clothing/under/fluff/taiga } -#{ -#ckey: kligor -#character_name: Andy Gettemy -#item_path: /obj/item/device/pda_mod/fluff/kligor -#} - { ckey: konabird character_name: Rischi @@ -560,7 +505,6 @@ item_path: /obj/item/clothing/accessory/medal/nobel_science/fluff/ashley } # ######## L CKEYS - { ckey: lm40 character_name: Kenzie Houser @@ -568,13 +512,6 @@ item_path: /obj/item/weapon/reagent_containers/hypospray/vial/kenzie req_access: 5 } -#Weapon Box. Permit Expired -#{ -#ckey: luminescentring -#character_name: Briana Moore -#item_path: /obj/item/weapon/storage/box/fluff/briana_moore -#} - { ckey: luminescentring character_name: Briana Moore @@ -612,19 +549,6 @@ character_name: Giliana Gamish item_path: /obj/item/clothing/head/fluff/molenar2 } -# Permit Expired -#{ -#ckey: molenar -#character_name: Kari Akiren -#item_path: /obj/item/weapon/gun/projectile/shotgun/pump/rifle/fluff/kari_akiren -#} -# -#{ -#ckey: molenar -#character_name: Kari Akiren -#item_path: /obj/item/clothing/accessory/permit/gun/fluff/kari_akiren -#} - { ckey: myryan character_name: Myryan Karnage-Cunningham @@ -638,24 +562,29 @@ item_path: /obj/item/clothing/suit/security/navyhos req_access: 58 } +{ +ckey: mr_signmeup +character_name: Daniel Fisher +item_path: /obj/item/clothing/accessory/medal/conduct +} + # ######## N CKEYS - { ckey: natje character_name: Awen Henry -item_path: /obj/item/clothing/head/fluff/awoo +item_path: /obj/item/clothing/head/fluff/wolfgirl } { ckey: natje character_name: Awen Henry -item_path: /obj/item/clothing/shoes/fluff/awoo +item_path: /obj/item/clothing/shoes/fluff/wolfgirl } { ckey: natje character_name: Awen Henry -item_path: /obj/item/clothing/under/fluff/awoo +item_path: /obj/item/clothing/under/fluff/wolfgirl } { @@ -678,20 +607,6 @@ item_path: /obj/item/weapon/melee/fluff/holochain } # ######## P CKEYS - -#{ -#ckey: pawoverlord -#character_name: Sorrel Cavalet -#item_path: /obj/item/clothing/accessory/permit/gun/fluff/sorrel_cavalet -#} - -#{ -#ckey: pawoverlord -#character_name: Sorrel Cavalet -#item_path: /obj/item/weapon/gun/projectile/colt -#item_desc: "A cheap Martian knock-off of a Colt M1911. It has the word 'Cavalet' etched on the side. Uses .45 rounds." -#} - { ckey: phoaly character_name: Lily Maximus @@ -703,6 +618,7 @@ ckey: pontifexminimus character_name: Lucius Null item_path: /obj/item/weapon/fluff/dragor_dot } + { ckey: pontifexminimus character_name: Lucia Null @@ -713,18 +629,12 @@ item_path: /obj/item/weapon/fluff/dragor_dot # ######## R CKEYS -# Permit Expired -#{ -#ckey: Razerwing -#character_name: Archer Maximus -#item_path: /obj/item/clothing/accessory/permit/gun/fluff/archermaximus -#} -# -#{ -#ckey: Razerwing -#character_name: Archer Maximus -#item_path: /obj/item/weapon/gun/projectile/colt/fluff/archercolt -#} +{ +ckey: radiantaurora +character_name: Tiemli Kroto +item_path: /obj/item/clothing/glasses/welding/tiemgogs +req_titles: Roboticist +} # ######## S CKEYS { @@ -757,6 +667,12 @@ character_name: Joseph Skinner item_path: /obj/item/clothing/under/rank/security/fluff/formalsec } +{ +ckey: semaun +character_name: Viktor Solothurn +item_path: /obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask/fluff/viktor +} + { ckey: scree character_name: Scree @@ -775,27 +691,6 @@ character_name: Alfonso Oak Telanor item_path: /obj/item/clothing/glasses/sunglasses/fluff/alfonso } -#{ -#ckey: silencedmp5a5 -#character_name: Serdykov Antoz -#item_path: /obj/item/clothing/head/helmet/serdy -#req_access = 1 -#} - -#{ -#ckey: silencedmp5a5 -#character_name: Serdykov Antoz -#item_path: /obj/item/clothing/suit/armor/vest/wolftaur/serdy -#req_access = 1 -#} -# -#{ -#ckey: silencedmp5a5 -#character_name: Serdykov Antoz -#item_path: /obj/item/weapon/storage/box/fluff/serdykov_antoz -#req_access = 1 -#} - { ckey: silencedmp5a5 character_name: NT-HASD #556 @@ -901,36 +796,43 @@ item_path: /obj/item/clothing/head/fluff/runac } # ######## U CKEYS + # ######## V CKEYS { ckey: vailthewolf character_name: Vakashi item_path: /obj/item/clothing/accessory/permit/gun/fluff/Vakashi } + { ckey: vailthewolf character_name: Vakashi item_path: /obj/item/weapon/reagent_containers/spray/pepper } + ckey: verkister character_name: Cameron Eggbert item_path: /obj/item/clothing/glasses/fluff/science_proper } + { ckey: verkister character_name: Cameron Eggbert item_path: /obj/item/weapon/disk/limb/eggnerdltd } + { ckey: verkister character_name: Opie Eggbert item_path: /obj/item/clothing/glasses/fluff/spiffygogs } + { ckey: virgo113 character_name: Verin Raharra item_path: /obj/item/clothing/suit/storage/hazardvest/fluff/verin } + { ckey: vorrarkul character_name: Lucina Dakarim @@ -992,11 +894,11 @@ item_path: /obj/item/clothing/mask/gas/sexymime } # ######## W CKEYS -#{ -#ckey: warbrand2 -#character_name: Brandy draca -#item_path: /obj/item/device/pda_mod/fluff/warbrand2 -#} +{ +ckey: wanderingdeviant +character_name: S'thasha Tavakdavi +item_path: /obj/item/clothing/accessory/bracer/fluff/xander_sthasha +} { ckey: werebear @@ -1035,7 +937,6 @@ item_path: /obj/item/weapon/implanter/reagent_generator/tempest } # ######## X CKEYS - { ckey: Xsdew character_name: Penelope Allen @@ -1055,6 +956,7 @@ item_path: /obj/item/clothing/accessory/medal/silver/unity } # ######## Y CKEYS + # ######## Z CKEYS { ckey: zekesturm @@ -1079,3 +981,10 @@ ckey: zodiacshadow character_name: Nehi Maximus item_path: /obj/item/device/radio/headset/fluff/zodiacshadow } + +{ +ckey: chaoko99 +character_name: Aika Hisakawa +item_path: /obj/item/clothing/suit/fluff/blue_trimmed_coat +} + diff --git a/config/example/config.txt b/config/example/config.txt index 5b316f8df2..06e02d14fc 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -413,6 +413,12 @@ ENGINE_MAP Supermatter Engine,Edison's Bane # Controls if the 'time off' system is used for determining if players can play 'Off-Duty' jobs (requires SQL) # TIME_OFF +# If 'time off' system is on, controls whether or not players can switch on/off duty midround using timeclocks +# PTO_JOB_CHANGE + +# Forbids players from joining if they have no set General flavor text +# REQUIRE_FLAVOR + # Applies a limit to the number of assistants and visitors respectively # LIMIT_INTERNS 6 # LIMIT_VISITORS 6 diff --git a/config/jobwhitelist.txt b/config/jobwhitelist.txt index e615f5eafd..859e8e2007 100644 --- a/config/jobwhitelist.txt +++ b/config/jobwhitelist.txt @@ -8,3 +8,4 @@ tinybear16 - clown chargae - mime verkister - clown H0lySquirr3l - clown +sgtryder - mime \ No newline at end of file diff --git a/html/changelog.html b/html/changelog.html index ef602977f9..71ae05fb38 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,335 @@ -->
      +

      07 September 2019

      +

      Heroman3003 updated:

      +
        +
      • Desk bells can now be picked up like normal items.
      • +
      • Desk bells can now be deconstructed with a wrench while on the ground.
      • +
      • Desk bells can now only be made out of steel.
      • +
      +

      MisterLayne updated:

      +
        +
      • Material weapons that should not conduct, do not conduct.
      • +
      • Glass shards now do damage when you attack with them, based on the type of gloves you are wearing.
      • +
      +

      Novacat updated:

      +
        +
      • Ports MREs from Baystation, and adds a few supply crates with them to cargo.
      • +
      • Adds liquidprotein rations, currently only found in emergency MREs.
      • +
      +

      Woodrat updated:

      +
        +
      • Mining vendor added to cargo foyer.
      • +
      • Explorer vendor added to research foyer.
      • +
      • Handful of more pipe clamps added to engineering (atmos monitoring and EVA storage).
      • +
      • Adjustment to layout of chapel mass driver.
      • +
      +

      chaoko99 updated:

      +
        +
      • Ported Bay's stomach pump.
      • +
      + +

      21 August 2019

      +

      Atermonera updated:

      +
        +
      • Atmospherics is now bigger, with more room for fun and sanity-destroying spaghetti!
      • +
      +

      Mechoid updated:

      +
        +
      • Exosuits are now targetted by turrets again.
      • +
      • Mining charge strength lowered, price increased. Can be upgraded through R&D laser components.
      • +
      • Searing damage type added. Energy axe now uses searing damage.
      • +
      • Survey points added. Vendor added for explorer use. Costs are universally 'lower' compared to the mining vendor due to the rarity of points.
      • +
      • Swiping an ID card on a cataloguer will transfer the points to the card.
      • +
      • Any melee energy weapon can now be made to use a cell and charge.
      • +
      • Energy Axe prototype added to R&D.
      • +
      • Anomalous 'hunting trap' added to R&D. Useful for an initial stun on mobs for explorer scanning.
      • +
      • Beartraps now stun for a quarter of a second, so they are actually useful again, due to resistability.
      • +
      • Emergency welding pack added. Incredibly slow, but requires no protection.
      • +
      • Random turf modifier added for mapping use. Random humanoid remains spawner added for mapping use.
      • +
      • Plants under obj/structure/flora now can be made to be harvestable / forage-able.
      • +
      • Sif trees now occasionally have fruit containing useful microbes. Microbes slow blood flow resulting in slower chemical absorption and blood loss.
      • +
      • Flora by default are now under mobs, on the same layer as turf decals. Flora can now also randomize their size, previously only codified in Sivian trees.
      • +
      • Moss tendrils added to Sif, making eye-plants more rare.
      • +
      • Multiple PoIs added. 'Blue shuttle down' variant, 'UFO Frigate', multiple geysers, cliffs, and two variants of SFR which take drastically different approaches to the SFR standard. A vault was added, too.
      • +
      • Vault PoIs are now in a template group.
      • +
      • Generic AI-controlled humanoids added. Subtype made for use on Sif, clad in weak armor and temperature resistance, used as 'mindless clones'.
      • +
      • More artifice added.
      • +
      • Fultons made to be [somewhat] sane. Now only useable planetside.
      • +
      • Graviton Visor moved to R&D from the mining equipment vendor. Silver and Diamond pickaxes removed.
      • +
      • Added multiple new organs for Prometheans. Removing them will cause them to take large amounts of Toxloss untill they are Regenerated using the active regeneration verb.
      • +
      • Organ: pneumatic network, a series of skeletal tubes with high pressure used to move nutrients and waste.
      • +
      • Organs: -regenesis systems, different 'networks' of clumped cellular matter that allow Prometheans to recover rapidly from small injuries. Large or numerous injuries cause un-needed stress, and pain.
      • +
      • Internal organs now properly add themselves to the internal organ-by-name list, the one referenced 99.9% of the time.
      • +
      • Active regeneration will now replace internal organs if they are nonexistant, as it does for limbs.
      • +
      +

      Nalarac updated:

      +
        +
      • Mech cable layer works again.
      • +
      • Added setting for turrets to fire upon downed/laying targets.
      • +
      +

      TheFurryFeline updated:

      +
        +
      • New orange jumpsuit for loadout that doesn't get locked to tracking.
      • +
      • Prison jumpsuit filepath changed accordingly and closets with obj updated.
      • +
      +

      Woodrat updated:

      +
        +
      • Made the solgov uniforms use accessories for denoting department instead of having several different sprites in the uniform dmi folder.
      • +
      • Removed redundant solgov uniforms from uniform.dmi and moved the sprites for the solgov uniforms to their own dmi.
      • +
      • Fixed storage vests and drop pouches disappearing when one rolls down a uniform.
      • +
      + +

      08 August 2019

      +

      Mechoid updated:

      +
        +
      • Adds two new integrated circuit components: an advanced Text to Speech, and a Sign-Language reading camera.
      • +
      +

      Nalarac updated:

      +
        +
      • Lowered Chance of bots being emagged during ion storm.
      • +
      • Changes space helmet cameras to be a verb instead of on toggling lights.
      • +
      • Toggling helmet camera resets user upon activation (so you can have a new user).
      • +
      • Medibots will no longer attempt to heal FBPs.
      • +
      • Heavy-duty cell chargers can be built and upgraded.
      • +
      • Cyborgs can upgrade rechargers now.
      • +
      • Security bots will smack attackers once more.
      • +
      • Security bots will cable cuff people with hardsuit gauntlets instead of stun-locking.
      • +
      • Cut real time for security bots demanding surrender in half (6 seconds).
      • +
      • Updates the Supermatter Engine Operating Manual.
      • +
      +

      mistyLuminescence updated:

      +
        +
      • Serenity mech can now be built by robotics.
      • +
      + +

      30 July 2019

      +

      Atermonera updated:

      +
        +
      • Added a shutoff monitoring console, which displays the status and location of all automatic shutoff valves. Currently mapped into the atmos monitoring room and the auxiliary engineering room on deck 1.
      • +
      + +

      27 July 2019

      +

      Mechoid updated:

      +
        +
      • Pipes will now leak if their ends are unsealed.
      • +
      • Added stasis clamps, which act as valves that can be placed on existing straight pipe segments.
      • +
      • Added automatic shutoff valves, which can automatically close if a leak appears anywhere on their pipe network.
      • +
      • Added a Xenobio-compatible ED-209.
      • +
      +

      Nalarac updated:

      +
        +
      • Repairing bots (like Beepsky) works now.
      • +
      • Emagged bots can be repaired with proximity sensors.
      • +
      • Combat mechs can punch more things. Mech punch sounds like juggernaut now
      • +
      • Simple mobs can attack more things.
      • +
      • Cult pylons take damage from bullets.
      • +
      • Click delay added on attacking simple doors and security barricades.
      • +
      • Material doors and barricades have different attacked sounds depending if its metal, wood, or resin.
      • +
      • Fixes ability of cyborg meson to see holes in ceiling.
      • +
      • Gives cyborgs a roofing synthesizer.
      • +
      • Husking bodies requires more burn.
      • +
      • Ripley speed boost module that can be built in robotics.
      • +
      • Added transmission and capacitance SMES coils to supply console.
      • +
      • Adds laser tag turrets orderable from cargo.
      • +
      • Turrets will shoot people laying down if emagged or set to lethal.
      • +
      • Turrets can not be lethal if built with non-lethal weapon.
      • +
      • Alien pistol fire noise moved to projectile. Now the turret will use the proper noise.
      • +
      • Turrets will have new colors based on projectile used.
      • +
      • Projectile lastertag is no more. It is now properly lasertag.
      • +
      • Integrated circuit printers no longer take fractions of a sheet.
      • +
      +

      Schnayy updated:

      +
        +
      • Replaces Cindi Kate's and Boot Knives bar sign sprites with new ones.
      • +
      • Added new clothes to the loadout: cardigans, pleated skirt, lilac dress, ugly Christmas sweater, flowery sweater, and red turtleneck.
      • +
      + +

      19 July 2019

      +

      Nalarac updated:

      +
        +
      • Add xenobio/xenobot to farmbots.
      • +
      +

      Woodrat updated:

      +
        +
      • Added a clotting kit to the Raider Shuttle.
      • +
      • Swapped out the ai turrets for industrial turrets in the teleporter rooms.
      • +
      • Adjusted access to the turret controls to heads of staff access, moved them outside of the rooms.
      • +
      + +

      07 July 2019

      +

      Heroman3003 updated:

      +
        +
      • Broken components now have matter worth of 1000 steel units (half a sheet).
      • +
      • Components dropped from loot piles will now use random proper sprite instead of default placeholder.
      • +
      +

      mistyLuminescence updated:

      +
        +
      • Now available in Cargo: xenoarch technology (100 points) and tactical light armor (75 points)!
      • +
      • Tactical light armor is like regular tactical armor, but lighter (a full set is 0.5 slowdown), warmer (protects against moderate snow) and a little less protective.
      • +
      • Leg guards can now store bootknives.
      • +
      • Xenoarch brushes and pickaxes now have a reasonable force rating instead of hitting like a truck.
      • +
      • Adds the 'POI - ' prefix to the POI location names to make it easier for ghosts to teleport to them- e.g. 'POI - Rocky2' rather than just 'Rocky2'.
      • +
      • Fixes a runtime caused by attempting to use a crew monitor console while on an invalid Z-level.
      • +
      • Fixes GPSes not detecting other GPSes while on an invalid Z-level. Transit Z is unaffected by this- no spying on other shuttles!
      • +
      + +

      23 June 2019

      +

      Mechoid updated:

      +
        +
      • E-swords and Changeling arm-weapons can now block projectiles (again).
      • +
      +

      Novacat updated:

      +
        +
      • Adds greyscale pills, which are colored by their reagent
      • +
      • Adds colored pill bottles, most pre-spawned pill bottles are colored
      • +
      • Prespawn pills and pill bottles have had their names simplified
      • +
      • Attempts to make lighting less uniform
      • +
      • Cleans up lighting code
      • +
      +

      TheFurryFeline updated:

      +
        +
      • Fixes infinite frame production for some machinery objects such as grounding rods or exonet nodes. If there's no available circuit board to get, then don't return anything when attempting deconstruction. Ported from Cit RP. Eliminates 'Cannot read null.board_type' runtimes where they apply.
      • +
      • Allows hydroponic machines to be unwrenched.
      • +
      +

      Woodrat updated:

      +
        +
      • Turrets and respective controls added to each teleporter room.
      • +
      • Air tank on shuttles switched out with an air canister.
      • +
      • Roundstart Tcomms SMES starts fully charged.
      • +
      • Air tanks on station have the same amount in them as air canisters.
      • +
      • Expanded atmospheric tanks to be 2 by 3 instead of 2 by 2.
      • +
      • Wilderness Shuttle landing point shelter updated.
      • +
      • Teleporter room hand tele can now spawn in one of 4 places.
      • +
      • Halved the warm up time when the autopilot on the shuttles start at round start from ten minutes to five and nine respectively. Should mean the first shuttle will be taking from the outpost when the second shuttle leaves the station.
      • +
      • Reduced transit time from 75 seconds with pilot, 150 seconds without to 60 and 120 respectively.
      • +
      • Amount of phoron in the outposts reduced.
      • +
      • Ground Xenobio and Xenoflora torn down partially.
      • +
      • Reduction of the amount of steel and glass in engineering by half.
      • +
      • Reduction of the amount of steel and glass in EVA by half.
      • +
      • Fix for Dock 2 Transfer Shuttle airlock buttons not working.
      • +
      • Intercoms added to arrivals shuttle.
      • +
      • Modular computers scattered around the main station.
      • +
      • Elevator can be accessed at centcomm.
      • +
      • Fix Bridge Secretary Quarters tint.
      • +
      • Landmarks for Bluespacerift added.
      • +
      • Wilderness Shuttle landing sides should no longer shiskabob shuttles.
      • +
      • Modular Computers replacing wrong computers.
      • +
      • Chapel Mass Driver.
      • +
      • Merc Turrets function properly.
      • +
      • Map issues on the main outpost.
      • +
      +

      mistyLuminescence updated:

      +
        +
      • Splints now work on all areas of the body.
      • +
      • When the supermatter explodes, it now leaves behind a shattered plinth that continues to emit radiation. You should probably get rid of it.
      • +
      • If you destroy the supermatter early (e.g. through admin deletion shenanigans), it no longer irradiates everyone forever.
      • +
      • Welding lockers now actually updates their sprites properly.
      • +
      • Every floor tile now has a minor (2%) chance to spawn with some dirt on it. The Janitor now has a job again.
      • +
      • Similarly, every Sif grass tile now has a minor (5%) chance to spawn with some fancy eyebulb grass on it. It can be removed by clicking on it with harm intent.
      • +
      • Adds cats-in-boxes, which can be activated (once) to spawn cats. There's an orange tabby (Cargo cats) and a tuxedo (Runtime) version.
      • +
      • Fixes a material duplication exploit.
      • +
      + +

      04 June 2019

      +

      Chaoko99 updated:

      +
        +
      • The medibot's minimum configurable threshhold has been lowered to 0, from 5. Perfect for silly-billies coming into medical with 2 brute boo boos, thanks to accidentally throwing something at themselves or.. something.
      • +
      +

      Heroman3003 updated:

      +
        +
      • Restricts Alarm Monitor modular computer program to engineering access.
      • +
      • Removed access to departmental networks on camera modular computer program for average people.
      • +
      • Added access to full network on camera modular computer program for heads.
      • +
      +

      Mechoid updated:

      +
        +
      • Borers can speak through nearby mobs if they have a sufficient build-up of chemicals.
      • +
      • Borers have a max chemical volume.
      • +
      • The Sakimm, Frostfly, Glitterfly, River Leech, Crystal-Feathered Duck, and Kururak are now able to be used on the map and PoIs.
      • +
      • Glass jars can now hold glitterflies, river leeches, and frostflies.
      • +
      • Glass jars now respect mobs that exist over the lighting plane, adding a special overlay visible when on the ground.
      • +
      +

      Nalarac updated:

      +
        +
      • Closed all bugs on tracker that are no longer relevant or a bug.
      • +
      +

      Novacat updated:

      +
        +
      • Attempts to fix the timer SS to be moore robust
      • +
      • Fixes bug with custom laptops.
      • +
      • Raises cost of the elite model to 7 loadout points.
      • +
      +

      chaoko99 updated:

      +
        +
      • Adds the clientside 'ping' and 'reconnect' commands to the file menu.
      • +
      +

      mistyLuminescence updated:

      +
        +
      • Prone people can no longer interact with an empty hand.
      • +
      • Two-handed items are now correctly unwielded when the user's offhand is severed.
      • +
      • Mecha syringe guns now respect pierceproof clothing.
      • +
      • Securitrons now correctly ignore attacks when disabled.
      • +
      • Rechargers now correctly benefit from upgraded capacitors.
      • +
      • Medical record laptops and detective TV camera consoles no longer turn into regular consoles when de- and re-constructed.
      • +
      • Zaddat Shrouds now retain their base name when damaged, if the shroud has been customized.
      • +
      • Adds the makeover kit to the traitor uplink for 5 TC, also available in the Infiltrator bundle null crate in Cargo. Makeover kits can be used to change the user's hair style and color, and eye color.
      • +
      • Random event timers now wait until the round starts to begin counting down.
      • +
      + +

      06 May 2019

      +

      Mechoid updated:

      +
        +
      • Added fishing mechanics, and fishing gear.
      • +
      • Cloth can make normal cloth things.
      • +
      +

      Novacat updated:

      +
        +
      • Ports Bay code to allow hiding accessories when jumpsuits are rolled down/sleeve rolled.
      • +
      • Ports medals and ribbons from SEV Torch.
      • +
      • Ports initial infrastructure for modular computers from Baystation.
      • +
      • Cameras now have a slight static when viewing through them.
      • +
      • Adds Digital Warrant Program, to set warrants. Comes with a device.
      • +
      • Adds Supermatter Monitor program.
      • +
      • Chemistry processes instantly again.
      • +
      • Nanoui sends stuff even after client connect.
      • +
      • Shortwave radios now can transmit across connected Z levels.
      • +
      • Relays on moving platforms (shuttles) will now function.
      • +
      + +

      26 April 2019

      +

      Heroman3003 updated:

      +
        +
      • Allows voidsuits to have parts attached and removed while held in hand (but still not when worn).
      • +
      • Syringes will now immediately inject 5 units per injection into reagent containers. Does not change how injecting mobs work
      • +
      • Tape rolls can now be used on tiles with directional windows as long as they don't directly obstruct them.
      • +
      +

      Mechoid updated:

      +
        +
      • Reagents can now have a list of organs specified to slow their processing.
      • +
      • Reagents now process in the bloodstream at a rate determined by current pulse. No pulse, for any reason, will cause slightly slower processing if your species has a heart.
      • +
      • The heart organ determines the 'standard pulse' if the species has one.
      • +
      • Modifiers can set pulse directly, or shift it.
      • +
      • Adds Robobags and Corp-Tags to Robotics.
      • +
      • Re-adds the Space Worm with New and Improved [Doesn't Break The Universe] logic.
      • +
      • Sterilizine now hurts slimes, similar to water.
      • +
      +

      N3X15 updated:

      +
        +
      • Cyborgs can now use ladders regardless of whether they have a tool enabled or not.
      • +
      +

      Neerti updated:

      +
        +
      • During severe weather such as storms and blizzards, wind can cause you to move slower or faster in specific directions.
      • +
      • Weather application in the communicator displays wind direction and severity.
      • +
      • Holding umbrellas while in a storm no longer stuns you.
      • +
      +

      chaoko99 updated:

      +
        +
      • Ore bags will automatically pick up items when held, belted, or pocketed, and automatically deposit ore in boxes if one is being pulled.
      • +
      +

      17 April 2019

      Anewbe updated:

        diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index f569eb71bb..7c6b2ae448 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -4447,3 +4447,320 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: More jobs can now take certain items in the loadout. - rscadd: Adds departmental turtlenecks, available in department wardrobes or the loadout selection. Look good... in space. +2019-04-26: + Heroman3003: + - rscadd: Allows voidsuits to have parts attached and removed while held in hand + (but still not when worn). + - tweak: Syringes will now immediately inject 5 units per injection into reagent + containers. Does not change how injecting mobs work + - tweak: Tape rolls can now be used on tiles with directional windows as long as + they don't directly obstruct them. + Mechoid: + - rscadd: Reagents can now have a list of organs specified to slow their processing. + - rscadd: Reagents now process in the bloodstream at a rate determined by current + pulse. No pulse, for any reason, will cause slightly slower processing if your + species has a heart. + - rscadd: The heart organ determines the 'standard pulse' if the species has one. + - rscadd: Modifiers can set pulse directly, or shift it. + - rscadd: Adds Robobags and Corp-Tags to Robotics. + - rscadd: Re-adds the Space Worm with New and Improved [Doesn't Break The Universe] + logic. + - rscadd: Sterilizine now hurts slimes, similar to water. + N3X15: + - bugfix: Cyborgs can now use ladders regardless of whether they have a tool enabled + or not. + Neerti: + - rscadd: During severe weather such as storms and blizzards, wind can cause you + to move slower or faster in specific directions. + - rscadd: Weather application in the communicator displays wind direction and severity. + - rscdel: Holding umbrellas while in a storm no longer stuns you. + chaoko99: + - rscadd: Ore bags will automatically pick up items when held, belted, or pocketed, + and automatically deposit ore in boxes if one is being pulled. +2019-05-06: + Mechoid: + - rscadd: Added fishing mechanics, and fishing gear. + - tweak: Cloth can make normal cloth things. + Novacat: + - rscadd: Ports Bay code to allow hiding accessories when jumpsuits are rolled down/sleeve + rolled. + - rscadd: Ports medals and ribbons from SEV Torch. + - rscadd: Ports initial infrastructure for modular computers from Baystation. + - rscadd: Cameras now have a slight static when viewing through them. + - rscadd: Adds Digital Warrant Program, to set warrants. Comes with a device. + - rscadd: Adds Supermatter Monitor program. + - tweak: Chemistry processes instantly again. + - tweak: Nanoui sends stuff even after client connect. + - tweak: Shortwave radios now can transmit across connected Z levels. + - tweak: Relays on moving platforms (shuttles) will now function. +2019-06-04: + Chaoko99: + - tweak: The medibot's minimum configurable threshhold has been lowered to 0, from + 5. Perfect for silly-billies coming into medical with 2 brute boo boos, thanks + to accidentally throwing something at themselves or.. something. + Heroman3003: + - tweak: Restricts Alarm Monitor modular computer program to engineering access. + - tweak: Removed access to departmental networks on camera modular computer program + for average people. + - rscadd: Added access to full network on camera modular computer program for heads. + Mechoid: + - tweak: Borers can speak through nearby mobs if they have a sufficient build-up + of chemicals. + - tweak: Borers have a max chemical volume. + - rscadd: The Sakimm, Frostfly, Glitterfly, River Leech, Crystal-Feathered Duck, + and Kururak are now able to be used on the map and PoIs. + - tweak: Glass jars can now hold glitterflies, river leeches, and frostflies. + - tweak: Glass jars now respect mobs that exist over the lighting plane, adding + a special overlay visible when on the ground. + Nalarac: + - bugfix: Closed all bugs on tracker that are no longer relevant or a bug. + Novacat: + - bugfix: Attempts to fix the timer SS to be moore robust + - tweak: Fixes bug with custom laptops. + - tweak: Raises cost of the elite model to 7 loadout points. + chaoko99: + - rscadd: Adds the clientside 'ping' and 'reconnect' commands to the file menu. + mistyLuminescence: + - bugfix: Prone people can no longer interact with an empty hand. + - bugfix: Two-handed items are now correctly unwielded when the user's offhand is + severed. + - bugfix: Mecha syringe guns now respect pierceproof clothing. + - bugfix: Securitrons now correctly ignore attacks when disabled. + - bugfix: Rechargers now correctly benefit from upgraded capacitors. + - bugfix: Medical record laptops and detective TV camera consoles no longer turn + into regular consoles when de- and re-constructed. + - bugfix: Zaddat Shrouds now retain their base name when damaged, if the shroud + has been customized. + - rscadd: Adds the makeover kit to the traitor uplink for 5 TC, also available in + the Infiltrator bundle null crate in Cargo. Makeover kits can be used to change + the user's hair style and color, and eye color. + - bugfix: Random event timers now wait until the round starts to begin counting + down. +2019-06-23: + Mechoid: + - tweak: E-swords and Changeling arm-weapons can now block projectiles (again). + Novacat: + - rscadd: Adds greyscale pills, which are colored by their reagent + - rscadd: Adds colored pill bottles, most pre-spawned pill bottles are colored + - tweak: Prespawn pills and pill bottles have had their names simplified + - tweak: Attempts to make lighting less uniform + - tweak: Cleans up lighting code + TheFurryFeline: + - bugfix: Fixes infinite frame production for some machinery objects such as grounding + rods or exonet nodes. If there's no available circuit board to get, then don't + return anything when attempting deconstruction. Ported from Cit RP. Eliminates + 'Cannot read null.board_type' runtimes where they apply. + - tweak: Allows hydroponic machines to be unwrenched. + Woodrat: + - rscadd: Turrets and respective controls added to each teleporter room. + - tweak: Air tank on shuttles switched out with an air canister. + - tweak: Roundstart Tcomms SMES starts fully charged. + - tweak: Air tanks on station have the same amount in them as air canisters. + - tweak: Expanded atmospheric tanks to be 2 by 3 instead of 2 by 2. + - tweak: Wilderness Shuttle landing point shelter updated. + - tweak: Teleporter room hand tele can now spawn in one of 4 places. + - tweak: Halved the warm up time when the autopilot on the shuttles start at round + start from ten minutes to five and nine respectively. Should mean the first + shuttle will be taking from the outpost when the second shuttle leaves the station. + - tweak: Reduced transit time from 75 seconds with pilot, 150 seconds without to + 60 and 120 respectively. + - tweak: Amount of phoron in the outposts reduced. + - tweak: Ground Xenobio and Xenoflora torn down partially. + - rscdel: Reduction of the amount of steel and glass in engineering by half. + - rscdel: Reduction of the amount of steel and glass in EVA by half. + - bugfix: Fix for Dock 2 Transfer Shuttle airlock buttons not working. + - rscadd: Intercoms added to arrivals shuttle. + - rscadd: Modular computers scattered around the main station. + - rscadd: Elevator can be accessed at centcomm. + - bugfix: Fix Bridge Secretary Quarters tint. + - bugfix: Landmarks for Bluespacerift added. + - bugfix: Wilderness Shuttle landing sides should no longer shiskabob shuttles. + - bugfix: Modular Computers replacing wrong computers. + - bugfix: Chapel Mass Driver. + - bugfix: Merc Turrets function properly. + - bugfix: Map issues on the main outpost. + mistyLuminescence: + - tweak: Splints now work on all areas of the body. + - tweak: When the supermatter explodes, it now leaves behind a shattered plinth + that continues to emit radiation. You should probably get rid of it. + - bugfix: If you destroy the supermatter early (e.g. through admin deletion shenanigans), + it no longer irradiates everyone forever. + - bugfix: Welding lockers now actually updates their sprites properly. + - tweak: Every floor tile now has a minor (2%) chance to spawn with some dirt on + it. The Janitor now has a job again. + - tweak: Similarly, every Sif grass tile now has a minor (5%) chance to spawn with + some fancy eyebulb grass on it. It can be removed by clicking on it with harm + intent. + - rscadd: Adds cats-in-boxes, which can be activated (once) to spawn cats. There's + an orange tabby (Cargo cats) and a tuxedo (Runtime) version. + - bugfix: Fixes a material duplication exploit. +2019-07-07: + Heroman3003: + - rscadd: Broken components now have matter worth of 1000 steel units (half a sheet). + - tweak: Components dropped from loot piles will now use random proper sprite instead + of default placeholder. + mistyLuminescence: + - rscadd: 'Now available in Cargo: xenoarch technology (100 points) and tactical + light armor (75 points)!' + - rscadd: Tactical light armor is like regular tactical armor, but lighter (a full + set is 0.5 slowdown), warmer (protects against moderate snow) and a little less + protective. + - tweak: Leg guards can now store bootknives. + - tweak: Xenoarch brushes and pickaxes now have a reasonable force rating instead + of hitting like a truck. + - tweak: Adds the 'POI - ' prefix to the POI location names to make it easier for + ghosts to teleport to them- e.g. 'POI - Rocky2' rather than just 'Rocky2'. + - bugfix: Fixes a runtime caused by attempting to use a crew monitor console while + on an invalid Z-level. + - bugfix: Fixes GPSes not detecting other GPSes while on an invalid Z-level. Transit + Z is unaffected by this- no spying on other shuttles! +2019-07-19: + Nalarac: + - tweak: Add xenobio/xenobot to farmbots. + Woodrat: + - rscadd: Added a clotting kit to the Raider Shuttle. + - tweak: Swapped out the ai turrets for industrial turrets in the teleporter rooms. + - tweak: Adjusted access to the turret controls to heads of staff access, moved + them outside of the rooms. +2019-07-27: + Mechoid: + - rscadd: Pipes will now leak if their ends are unsealed. + - rscadd: Added stasis clamps, which act as valves that can be placed on existing + straight pipe segments. + - rscadd: Added automatic shutoff valves, which can automatically close if a leak + appears anywhere on their pipe network. + - rscadd: Added a Xenobio-compatible ED-209. + Nalarac: + - bugfix: Repairing bots (like Beepsky) works now. + - rscadd: Emagged bots can be repaired with proximity sensors. + - tweak: Combat mechs can punch more things. Mech punch sounds like juggernaut now + - tweak: Simple mobs can attack more things. + - bugfix: Cult pylons take damage from bullets. + - bugfix: Click delay added on attacking simple doors and security barricades. + - tweak: Material doors and barricades have different attacked sounds depending + if its metal, wood, or resin. + - tweak: Fixes ability of cyborg meson to see holes in ceiling. + - rscadd: Gives cyborgs a roofing synthesizer. + - tweak: Husking bodies requires more burn. + - rscadd: Ripley speed boost module that can be built in robotics. + - rscadd: Added transmission and capacitance SMES coils to supply console. + - rscadd: Adds laser tag turrets orderable from cargo. + - bugfix: Turrets will shoot people laying down if emagged or set to lethal. + - bugfix: Turrets can not be lethal if built with non-lethal weapon. + - bugfix: Alien pistol fire noise moved to projectile. Now the turret will use the + proper noise. + - tweak: Turrets will have new colors based on projectile used. + - tweak: Projectile lastertag is no more. It is now properly lasertag. + - bugfix: Integrated circuit printers no longer take fractions of a sheet. + Schnayy: + - tweak: Replaces Cindi Kate's and Boot Knives bar sign sprites with new ones. + - rscadd: 'Added new clothes to the loadout: cardigans, pleated skirt, lilac dress, + ugly Christmas sweater, flowery sweater, and red turtleneck.' +2019-07-30: + Atermonera: + - rscadd: Added a shutoff monitoring console, which displays the status and location + of all automatic shutoff valves. Currently mapped into the atmos monitoring + room and the auxiliary engineering room on deck 1. +2019-08-08: + Mechoid: + - rscadd: 'Adds two new integrated circuit components: an advanced Text to Speech, + and a Sign-Language reading camera.' + Nalarac: + - tweak: Lowered Chance of bots being emagged during ion storm. + - tweak: Changes space helmet cameras to be a verb instead of on toggling lights. + - rscadd: Toggling helmet camera resets user upon activation (so you can have a + new user). + - bugfix: Medibots will no longer attempt to heal FBPs. + - tweak: Heavy-duty cell chargers can be built and upgraded. + - bugfix: Cyborgs can upgrade rechargers now. + - bugfix: Security bots will smack attackers once more. + - tweak: Security bots will cable cuff people with hardsuit gauntlets instead of + stun-locking. + - tweak: Cut real time for security bots demanding surrender in half (6 seconds). + - tweak: Updates the Supermatter Engine Operating Manual. + mistyLuminescence: + - tweak: Serenity mech can now be built by robotics. +2019-08-21: + Atermonera: + - tweak: Atmospherics is now bigger, with more room for fun and sanity-destroying + spaghetti! + Mechoid: + - bugfix: Exosuits are now targetted by turrets again. + - tweak: Mining charge strength lowered, price increased. Can be upgraded through + R&D laser components. + - rscadd: Searing damage type added. Energy axe now uses searing damage. + - rscadd: Survey points added. Vendor added for explorer use. Costs are universally + 'lower' compared to the mining vendor due to the rarity of points. + - rscadd: Swiping an ID card on a cataloguer will transfer the points to the card. + - tweak: Any melee energy weapon can now be made to use a cell and charge. + - rscadd: Energy Axe prototype added to R&D. + - rscadd: Anomalous 'hunting trap' added to R&D. Useful for an initial stun on mobs + for explorer scanning. + - tweak: Beartraps now stun for a quarter of a second, so they are actually useful + again, due to resistability. + - rscadd: Emergency welding pack added. Incredibly slow, but requires no protection. + - rscadd: Random turf modifier added for mapping use. Random humanoid remains spawner + added for mapping use. + - rscadd: Plants under obj/structure/flora now can be made to be harvestable / forage-able. + - rscadd: Sif trees now occasionally have fruit containing useful microbes. Microbes + slow blood flow resulting in slower chemical absorption and blood loss. + - tweak: Flora by default are now under mobs, on the same layer as turf decals. + Flora can now also randomize their size, previously only codified in Sivian + trees. + - rscadd: Moss tendrils added to Sif, making eye-plants more rare. + - rscadd: Multiple PoIs added. 'Blue shuttle down' variant, 'UFO Frigate', multiple + geysers, cliffs, and two variants of SFR which take drastically different approaches + to the SFR standard. A vault was added, too. + - tweak: Vault PoIs are now in a template group. + - rscadd: Generic AI-controlled humanoids added. Subtype made for use on Sif, clad + in weak armor and temperature resistance, used as 'mindless clones'. + - rscadd: More artifice added. + - tweak: Fultons made to be [somewhat] sane. Now only useable planetside. + - tweak: Graviton Visor moved to R&D from the mining equipment vendor. Silver and + Diamond pickaxes removed. + - rscadd: Added multiple new organs for Prometheans. Removing them will cause them + to take large amounts of Toxloss untill they are Regenerated using the active + regeneration verb. + - rscadd: 'Organ: pneumatic network, a series of skeletal tubes with high pressure + used to move nutrients and waste.' + - rscadd: 'Organs: -regenesis systems, different ''networks'' of clumped cellular + matter that allow Prometheans to recover rapidly from small injuries. Large + or numerous injuries cause un-needed stress, and pain.' + - bugfix: Internal organs now properly add themselves to the internal organ-by-name + list, the one referenced 99.9% of the time. + - tweak: Active regeneration will now replace internal organs if they are nonexistant, + as it does for limbs. + Nalarac: + - bugfix: Mech cable layer works again. + - rscadd: Added setting for turrets to fire upon downed/laying targets. + TheFurryFeline: + - rscadd: New orange jumpsuit for loadout that doesn't get locked to tracking. + - tweak: Prison jumpsuit filepath changed accordingly and closets with obj updated. + Woodrat: + - tweak: Made the solgov uniforms use accessories for denoting department instead + of having several different sprites in the uniform dmi folder. + - tweak: Removed redundant solgov uniforms from uniform.dmi and moved the sprites + for the solgov uniforms to their own dmi. + - bugfix: Fixed storage vests and drop pouches disappearing when one rolls down + a uniform. +2019-09-07: + Heroman3003: + - tweak: Desk bells can now be picked up like normal items. + - tweak: Desk bells can now be deconstructed with a wrench while on the ground. + - tweak: Desk bells can now only be made out of steel. + MisterLayne: + - tweak: Material weapons that should not conduct, do not conduct. + - tweak: Glass shards now do damage when you attack with them, based on the type + of gloves you are wearing. + Novacat: + - rscadd: Ports MREs from Baystation, and adds a few supply crates with them to + cargo. + - rscadd: Adds liquidprotein rations, currently only found in emergency MREs. + Woodrat: + - rscadd: Mining vendor added to cargo foyer. + - rscadd: Explorer vendor added to research foyer. + - rscadd: Handful of more pipe clamps added to engineering (atmos monitoring and + EVA storage). + - tweak: Adjustment to layout of chapel mass driver. + chaoko99: + - rscadd: Ported Bay's stomach pump. diff --git a/html/changelogs/Anewbe - Bloodloss.yml b/html/changelogs/Anewbe - Bloodloss.yml deleted file mode 100644 index 8ec14a38db..0000000000 --- a/html/changelogs/Anewbe - Bloodloss.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Anewbe - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - bugfix: "You can no longer have ALL of your blood punched out." - - bugfix: "Haemophiliacs will no longer spontaneously have ALL of their blood go missing from ~90%." diff --git a/html/changelogs/Anewbe - Headgibs.yml b/html/changelogs/Anewbe - Headgibs.yml deleted file mode 100644 index 6aa097a172..0000000000 --- a/html/changelogs/Anewbe - Headgibs.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Anewbe - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Headgibbing is now determined by config" diff --git a/html/changelogs/Anewbe - Languages.yml b/html/changelogs/Anewbe - Languages.yml deleted file mode 100644 index da9a2f7b42..0000000000 --- a/html/changelogs/Anewbe - Languages.yml +++ /dev/null @@ -1,40 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Anewbe - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Certain languages now require assistance for a species to speak, but not understand, much like EAL." - - tweak: "Alai can only be `spoken` by Taj and Teshari." - - rscadd: "Adds a voicebox/larynx organ. Its only purpose at the moment is to assist in speaking certain langauges." - - tweak: "Language implants, like the EAL implant, now affect the voicebox organ, instead of being a freefloating implant." - - rscadd: "Adds a language implant for Common Skrellian." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Pilots.yml b/html/changelogs/Anewbe - Pilots.yml deleted file mode 100644 index 51684ce137..0000000000 --- a/html/changelogs/Anewbe - Pilots.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Anewbe -delete-after: True -changes - - tweak: "Pilot consoles now require pilot access to use." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Voice Changers.yml b/html/changelogs/Anewbe - Voice Changers.yml deleted file mode 100644 index 93d09587bf..0000000000 --- a/html/changelogs/Anewbe - Voice Changers.yml +++ /dev/null @@ -1,39 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Anewbe - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - bugfix: "Voice changing masks no longer give up when you put on a hardsuit." - - tweak: "Mask voice changers start on." - - tweak: "Mask voice changers that do not have a set voice will now default to your worn ID's name." - - tweak: "Mask voice changers now have a verb to reset their name." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Yet More Gibberish.yml b/html/changelogs/Anewbe - Yet More Gibberish.yml deleted file mode 100644 index d7472af1c3..0000000000 --- a/html/changelogs/Anewbe - Yet More Gibberish.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Anewbe - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "The hyphen (-) is no longer a default language key, as people use it to represent other things. If you are still having issues with Gibberish, reset your language keys, or at least make sure they don't include any characters you regularly start speech with." diff --git a/html/changelogs/Ater - stasisbags.yml b/html/changelogs/Ater - stasisbags.yml deleted file mode 100644 index 11023943e9..0000000000 --- a/html/changelogs/Ater - stasisbags.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Atermonera -delete-after: True -changes: - - bugfix: "Stasis bags don't squish organics with high pressure when organics try to seek shelter from flesh wounds." - - tweak: "Body- and stasis bags can be tread upon when open." diff --git a/html/changelogs/Atermonera - Ceilings.yml b/html/changelogs/Atermonera - Ceilings.yml deleted file mode 100644 index 760df592f5..0000000000 --- a/html/changelogs/Atermonera - Ceilings.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: Atermonera -delete-after: True -changes: - - rscadd: "Steel sheets can be used to construct Roofing Tiles" - - rscadd: "Roofing tiles can be used on tiles under open spaces or space tiles in multiZ maps to place a lattice and plating on the space above" - - rscadd: "Roofing tiles can be used on outdoor turfs to make them indoors" - - rscadd: "Both functions work together on multiZ maps with outdoor turfs, only one roofing tile is used per tile roofed." diff --git a/html/changelogs/Atermonera-pressure_protection.yml b/html/changelogs/Atermonera-pressure_protection.yml deleted file mode 100644 index b846aec6d8..0000000000 --- a/html/changelogs/Atermonera-pressure_protection.yml +++ /dev/null @@ -1,8 +0,0 @@ -author: Atermonera -delete-after: True -changes: - - rscadd: "Clothing now has pressure protection variables, that set lower and upper pressure bounds within which they will protect you from pressure-based harm." - - rscadd: "Protection falls off when exceeding the pressure limits from 100% protection at the boundary to 0% at 10% in excess (above the max or below the min) boundary." - - tweak: "Currently, only hat and suit slots are checked for this protection (No change)." - - tweak: "Anomaly suits (The orange ones) now offer limited pressure protection in case anomalies start making or draining air." - - bugfix: "Firesuits are no longer spaceproof, but still offer protection against the high pressures of fire." diff --git a/html/changelogs/Cerebulon - Clothes.yml b/html/changelogs/Cerebulon - Clothes.yml deleted file mode 100644 index b05070efd1..0000000000 --- a/html/changelogs/Cerebulon - Clothes.yml +++ /dev/null @@ -1,8 +0,0 @@ - - -author: Cerebulon - -delete-after: True - -changes: - - rscadd: "Added 12 new loadout items plus colour variants: Utility pants, sleek overalls, sari, modern wrap jacket, ascetic garb, asymmetrical jackets, cowled vest, kamishimo, jingasa, maang tikka, thin-frame glasses, rimless glasses." diff --git a/html/changelogs/Cerebulon - Namelists.yml b/html/changelogs/Cerebulon - Namelists.yml deleted file mode 100644 index 6fd7911131..0000000000 --- a/html/changelogs/Cerebulon - Namelists.yml +++ /dev/null @@ -1,7 +0,0 @@ - -author: Cerebulon - -delete-after: True - -changes: - - rscadd: "Added 1000+ surnames and 1200+ forenames from various world cultures to human namelists" diff --git a/html/changelogs/Cerebulon - ReligionUpdate.yml b/html/changelogs/Cerebulon - ReligionUpdate.yml deleted file mode 100644 index 50f402fae0..0000000000 --- a/html/changelogs/Cerebulon - ReligionUpdate.yml +++ /dev/null @@ -1,15 +0,0 @@ - -author: Cerebulon - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Added customizable religious icons to the chaplain's office." - - rscadd: "Added black and white candles." - - tweak: "Updated religion lists in character setup to be lore friendly." diff --git a/html/changelogs/Cerebulon -Teshrigs.yml b/html/changelogs/Cerebulon -Teshrigs.yml deleted file mode 100644 index 1f4b499866..0000000000 --- a/html/changelogs/Cerebulon -Teshrigs.yml +++ /dev/null @@ -1,7 +0,0 @@ - -author: Cerebulon - -delete-after: True - -changes: - - rscadd: "Added teshari hardsuit sprites." diff --git a/html/changelogs/Eguns - Anewbe.yml b/html/changelogs/Eguns - Anewbe.yml deleted file mode 100644 index 9454b66ccd..0000000000 --- a/html/changelogs/Eguns - Anewbe.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Anewbe - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Eguns take 4 shots to stun, rather than 3." diff --git a/html/changelogs/Heroman3003 - borgclimbing.yml b/html/changelogs/Heroman3003 - borgclimbing.yml deleted file mode 100644 index d65dad24bd..0000000000 --- a/html/changelogs/Heroman3003 - borgclimbing.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: N3X15 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - bugfix: "Cyborgs can now use ladders regardless of whether they have a tool enabled or not." diff --git a/html/changelogs/Heroman3003 - handsuits.yml b/html/changelogs/Heroman3003 - handsuits.yml deleted file mode 100644 index 88c31cc072..0000000000 --- a/html/changelogs/Heroman3003 - handsuits.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Heroman3003 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Allows voidsuits to have parts attached and removed while held in hand (but still not when worn)." diff --git a/html/changelogs/Heroman3003 - syringetweak.yml b/html/changelogs/Heroman3003 - syringetweak.yml deleted file mode 100644 index 4c0e623ee2..0000000000 --- a/html/changelogs/Heroman3003 - syringetweak.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Heroman3003 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Syringes will now inject 5 units per injection and immedeately when injecting into reagent containers. Does not chage way injecting mobs work" \ No newline at end of file diff --git a/html/changelogs/Heroman3003 - windowtapes.yml b/html/changelogs/Heroman3003 - windowtapes.yml deleted file mode 100644 index 1333b0ac7c..0000000000 --- a/html/changelogs/Heroman3003 - windowtapes.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Heroman3003 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Tape rolls can now be used on tiles with directional windows as long as they don't directly obstruct them." diff --git a/html/changelogs/Hubblenaut - airlocks.yml b/html/changelogs/Hubblenaut - airlocks.yml deleted file mode 100644 index 2091ab6596..0000000000 --- a/html/changelogs/Hubblenaut - airlocks.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Hubblenaut - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "If a cycling airlock is already near the target pressure, pressing the buttons will toggle the doors instead of making it reenter the cycle process." diff --git a/html/changelogs/KasparoVv - PR - 4130.yml b/html/changelogs/KasparoVv - PR - 4130.yml deleted file mode 100644 index 6a46094651..0000000000 --- a/html/changelogs/KasparoVv - PR - 4130.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: KasparoVy -delete-after: True -changes: - - rscadd: "Hiding mobs are now revealed when grabbed, buckled and/or killed." - - rscadd: "You cannot hide if you grab something, are grabbed yourself or are buckled." - - rscadd: "Adds some fluff messages for when hiding mobs are revealed." diff --git a/html/changelogs/KasparoVv - PR - 5429.yml b/html/changelogs/KasparoVv - PR - 5429.yml deleted file mode 100644 index 10bcc76fbb..0000000000 --- a/html/changelogs/KasparoVv - PR - 5429.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: KasparoVv -delete-after: True -changes: - - rscadd: "You can now change the order of your body markings at character creation with the push of a button. Shift markings up or down layers at will to design the character you've always wanted, more easily than ever before." \ No newline at end of file diff --git a/html/changelogs/Kates - Hedgetrimmers.yml b/html/changelogs/Kates - Hedgetrimmers.yml deleted file mode 100644 index 142f879e40..0000000000 --- a/html/changelogs/Kates - Hedgetrimmers.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: LBnesquik - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Remplaced the plant clippers with a reskinned pair of hedgetrimmers." diff --git a/html/changelogs/Lbnesquik - Botany improvements #5662.yml b/html/changelogs/Lbnesquik - Botany improvements #5662.yml deleted file mode 100644 index 980a849850..0000000000 --- a/html/changelogs/Lbnesquik - Botany improvements #5662.yml +++ /dev/null @@ -1,44 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Lbnesquik - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "General biogenerator improvements:" - - rscadd: "Added feedback noise to the processing." - - rscadd: "Allow for cream dispensing." - - rscadd: "Allow for plant bag creation." - - rscadd: "Allow for 5 units of meat to be dispensed at once." - - rscadd: "Allow for 5 units of liquids to be dispensed at once totalling 50u." - - rscadd: "Add and allow the creation of larger plant bags for easier ferrying of plants.." - - rscadd: "Add a description to the machine itself." - - bugfix: "Fix a typo." diff --git a/html/changelogs/Leshana - vplk-airlocks-cycle-to-ext.yml b/html/changelogs/Leshana - vplk-airlocks-cycle-to-ext.yml deleted file mode 100644 index 3f59f30fea..0000000000 --- a/html/changelogs/Leshana - vplk-airlocks-cycle-to-ext.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Leshana and mustafakalash -delete-after: True -changes: - - rscadd: "A new 'planetary' mode for airlocks which makes them purge the chamber contents to the exterior atmosphere before filling with clean air, and vice versa." diff --git a/html/changelogs/Leshana-vplk-persistence.yml b/html/changelogs/Leshana-vplk-persistence.yml deleted file mode 100644 index 3e396d6634..0000000000 --- a/html/changelogs/Leshana-vplk-persistence.yml +++ /dev/null @@ -1,9 +0,0 @@ -author: Leshana -delete-after: True -changes: - - rscadd: "Enables round-to-round persistence of a few aspects of characters. More accurately, it automates updating your character setup. None of this code does anything you could not already do manually on the Character Setup screen, it simply does it automatically for you. ALL of these changes are optional, toggled on the VORE tab of character setup." - - rscadd: "Your late-join spawn location is determined by which cryo/elevator/etc you used to leave last time. Departing thru the elevators will set your spawn location to elevators etc." - - rscadd: "Your weight is saved (also any extra or deficient nutrition is resolved into weight gain/loss)" - - rscadd: "Your limbs settings are updated based on your status at end of round (whether limbs are normal, missing, robotic, etc)" - - rscadd: "Your markings are saved so they will be the same as when they were at end of round." - - tweak: "Replaced hard coded numbers for weight gain with constant defines." diff --git a/html/changelogs/Leshana-vplk-process-debug-verbs.yml b/html/changelogs/Leshana-vplk-process-debug-verbs.yml deleted file mode 100644 index af0ffd3f9d..0000000000 --- a/html/changelogs/Leshana-vplk-process-debug-verbs.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Leshana -delete-after: True -changes: - - rscadd: "Added admin verbs for debugging the scheduled process controllers." diff --git a/html/changelogs/Mechoid - Custom Rigs.yml b/html/changelogs/Mechoid - Custom Rigs.yml deleted file mode 100644 index 54ce0704cc..0000000000 --- a/html/changelogs/Mechoid - Custom Rigs.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Added a RIG customization kit." - - tweak: "RIGs now use a var called suit_state to determine the basis for their component icons, rather than the rig's icon state." diff --git a/html/changelogs/Mechoid - Flashes.yml b/html/changelogs/Mechoid - Flashes.yml deleted file mode 100644 index 9a027fc83f..0000000000 --- a/html/changelogs/Mechoid - Flashes.yml +++ /dev/null @@ -1,39 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Flashes now actually run the risk of burning out." - - tweak: "Flashes that burn out can be repaired after approximately 40 seconds and with some luck, via a screwdriver." - - tweak: "Cyborg flashes use charge only, and do not have the 10 flash limit. They instead begin burning large amounts of charge." - - tweak: "Flashes use miniscule charge in addition to their capacitor burn-out, totalling 12 uses, taking 4 to down a human. They can be charged within a standard charger." \ No newline at end of file diff --git a/html/changelogs/Mechoid - Gigaphone.yml b/html/changelogs/Mechoid - Gigaphone.yml deleted file mode 100644 index 4b4822f36f..0000000000 --- a/html/changelogs/Mechoid - Gigaphone.yml +++ /dev/null @@ -1,7 +0,0 @@ - -author: Mechoid - -delete-after: True - -changes: - - rscadd: "Added the Gigaphone. Currently unused in any PoIs or elsewhere." diff --git a/html/changelogs/Mechoid - LingJacket.yml b/html/changelogs/Mechoid - LingJacket.yml deleted file mode 100644 index 5aef776f8e..0000000000 --- a/html/changelogs/Mechoid - LingJacket.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - bugfix: "Oversight regarding Changeling revive not removing restraints fixed." - - tweak: "Straight Jackets can now be resisted out of in 8 minutes, or as low as 5 depending on species attacks. Shredding or hulked humans will break out in 20 seconds, destroying the jacket." diff --git a/html/changelogs/Mechoid - Medical Expansion.yml b/html/changelogs/Mechoid - Medical Expansion.yml new file mode 100644 index 0000000000..5d9b3b6aed --- /dev/null +++ b/html/changelogs/Mechoid - Medical Expansion.yml @@ -0,0 +1,59 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Mechoid + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Multiple new organs added. Humans and Skrell received spleens, all species expected to have a stomach and intestine organ have them." + - tweak: "Augment 'slots' organized." + - rscadd: "Multiple augments added, currently only available in the Traitor / Mercenary uplinks as easy-to-install implants." + - rscadd: "Anesthetic / Oxygen pumps added. A mobile stabilizer has been added. All three are available in Cargo." + - rscadd: "Medical MRE rations added to Cargo." + - tweak: "Roundstart implants now become invisible until being handled." + - rscadd: "Neural implant added to loadout, makes the brain of the user count as an assisted organ. Does not affect MMIs or their subtypes." + - rscadd: "Bioprinters now unlock more organs upon being upgraded, once they pass the anomalous tier, they unlock quite probably illegal organs." + - rscadd: "Three medical exosuit components have been added. Crisis and Hazmat response drones, and a mounted advanced medical analyzer." + - tweak: "Medical analyzers now detect on-skin reagents." + - rscadd: "Multiple new chemicals added, two used for upgrading bandage kits." + - rscadd: "Brute-based medical stacks can be upgraded." + - rscadd: "Crude brute kits can be made with cloth." + - tweak: "Stacks can now pass their colors onto objects produced by them. (Colored cloth, painted wood, etcetera.)" + - rscadd: "Two combined surgical tools added, for opening / closing ribcages and skulls, and removing organs respectively." + - rscadd: "Two dud implants added to the loadout. They do literally nothing but hurt you if you're EMP'd." + - experiment: "The larynx now controls the ability to speak. Damage to it will stop you from being able to speak anything but non-verbal languages." + - tweak: "Damage to the brain can now affect the pulse." + - tweak: "Only the critical blood level causes toxin damage, meaning individuals who die of standard oxygen loss from bloodloss can possibly be saved, but exsanguinated persons are unlikely." + - tweak: "Bioaugment.dm is now just augment.dm" + - tweak: "Robotic hearts do not have a pulse." + - tweak: "Brain damage can now cause loss of breath." + - tweak: "Paracetamol is now the precursor to tramadol." + - rscadd: "Calcium is now in the chemical vendor." diff --git a/html/changelogs/Mechoid - OrgansAndChems.yml b/html/changelogs/Mechoid - OrgansAndChems.yml deleted file mode 100644 index 0cc59e11f1..0000000000 --- a/html/changelogs/Mechoid - OrgansAndChems.yml +++ /dev/null @@ -1,39 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Reagents can now have a list of organs specified to slow their processing." - - rscadd: "Reagents now process in the bloodstream at a rate determined by current pulse. No pulse, for any reason, will cause slightly slower processing if your species has a heart." - - rscadd: "The heart organ determines the 'standard pulse' if the species has one." - - rscadd: "Modifiers can set pulse directly, or shift it." diff --git a/html/changelogs/Mechoid - Robobags.yml b/html/changelogs/Mechoid - Robobags.yml deleted file mode 100644 index ec3a6e287e..0000000000 --- a/html/changelogs/Mechoid - Robobags.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Adds Robobags and Corp-Tags to Robotics." diff --git a/html/changelogs/Mechoid - Sterilizine.yml b/html/changelogs/Mechoid - Sterilizine.yml deleted file mode 100644 index 51ea727eb1..0000000000 --- a/html/changelogs/Mechoid - Sterilizine.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Sterilizine now hurts slimes, similar to water." diff --git a/html/changelogs/Mechoid - Surgical.yml b/html/changelogs/Mechoid - Surgical.yml deleted file mode 100644 index 844f31e035..0000000000 --- a/html/changelogs/Mechoid - Surgical.yml +++ /dev/null @@ -1,7 +0,0 @@ - -author: Mechoid - -delete-after: True - -changes: - - rscadd: "Adds a new surgical procedure for fixing brute and burn on limbs." diff --git a/html/changelogs/Mechoid - Virtual and Alien Reality.yml b/html/changelogs/Mechoid - Virtual and Alien Reality.yml deleted file mode 100644 index c1149647b5..0000000000 --- a/html/changelogs/Mechoid - Virtual and Alien Reality.yml +++ /dev/null @@ -1,43 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Organs can now add or remove verbs." - - rscadd: "Organs now can have special effects on processing." - - tweak: "Xenomorph plasma vessels now regenerate phoron passively, at an incredibly slow rate. Can be accelerated by consuming liquid phoron." - - tweak: "Xenomorph organs now give their respective abilities when implanted." - - tweak: "Virtual Reality pods cleaned up." - - rscadd: "Alien Realiy pod added for PoIs or admins to use. Functions similarly to a VR pod, except it will produce a living body on the pod's turf." - - rscadd: "Three replicant species added for event and PoI use. They are shape-shifters each with different abilities due to their organs." - - rscadd: "Replicant organs added, versions of many organs that do not reject in their host. Two currently exist that give special passives." diff --git a/html/changelogs/Mechoid-DAXONS.yml b/html/changelogs/Mechoid-DAXONS.yml deleted file mode 100644 index c45fc54322..0000000000 --- a/html/changelogs/Mechoid-DAXONS.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Tweaked Respiro- Cordra- Gastiro- and Hepanephro- Daxons to be more effective with dealing with the side-effects of damaged organs." diff --git a/html/changelogs/Mechoid-Grippers.yml b/html/changelogs/Mechoid-Grippers.yml deleted file mode 100644 index 1b4c6f69b3..0000000000 --- a/html/changelogs/Mechoid-Grippers.yml +++ /dev/null @@ -1,13 +0,0 @@ - -author: Mechoid - -delete-after: True - - -changes: - - rscadd: "Research and Engineering borgs now have 'Circuit Grippers', used for constructing and operating integrated circuit machinery." - - tweak: "Grippers now allow interaction with their contents, by attacking the gripper itself with an item when one is held. I.E., drawing from a beaker with a syringe." - - tweak: "Grippers now show their held item's examine information when examined. Tab information added." - - tweak: "Cyborgs can now interact with integrated circuit printers and machines when adjacent to them." - - tweak: "Multitools now have a menu to switch modes between standard use and integrated circuit reference scanning. Antag multi-tools untouched for now." - - bugfix: "Integrated circuit printer now respects adjacency, if it is not set to debug." diff --git a/html/changelogs/Mechoid-PromBlud.yml b/html/changelogs/Mechoid-PromBlud.yml deleted file mode 100644 index e08778ff4d..0000000000 --- a/html/changelogs/Mechoid-PromBlud.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Prometheans are no longer murdered by blood, and instead process it like a weak nutrient. Will slow the regeneration of toxins due to water content." diff --git a/html/changelogs/Mechoid-RSFs.yml b/html/changelogs/Mechoid-RSFs.yml deleted file mode 100644 index 72c7ac0b09..0000000000 --- a/html/changelogs/Mechoid-RSFs.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Ctrl clicking a Rapid Service Fabricator when held will allow you to choose the container it deploys." - - bugfix: "The Rapid Service Fabricator's icon is correctly set." diff --git a/html/changelogs/Mechoid-Slimefix.yml b/html/changelogs/Mechoid-Slimefix.yml deleted file mode 100644 index 10bb77d5bc..0000000000 --- a/html/changelogs/Mechoid-Slimefix.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - bugfix: "Slimes now respect slime colors as their faction when dealing with other slimes." diff --git a/html/changelogs/Mechoid-Tasers.yml b/html/changelogs/Mechoid-Tasers.yml deleted file mode 100644 index 422d2569e3..0000000000 --- a/html/changelogs/Mechoid-Tasers.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Mechoid - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Security taser shot count dropped from 10 to 5." diff --git a/html/changelogs/Mewchild - PR - 5396.yml b/html/changelogs/Mewchild - PR - 5396.yml deleted file mode 100644 index 75998b87c9..0000000000 --- a/html/changelogs/Mewchild - PR - 5396.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Mewchild -delete-after: True -changes: - - rscadd: "Ports several AI core sprites from ages and places past" diff --git a/html/changelogs/Neerti-AI.yml b/html/changelogs/Neerti-AI.yml deleted file mode 100644 index 9b9f13674d..0000000000 --- a/html/changelogs/Neerti-AI.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Neerti - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - experiment: "Rewrites the AI system for mobs. It should be more responsive and robust. Some mobs have special AIs which can do certain things like kiting, following you on a lark, or telling you to go away before shooting you." - - tweak: "Rewrites literally every 'simple_animal' mob into new, shinier, and hopefully more interesting 'simple_mob' mobs, some with new mechanics, like spiders, fake mechas, and hivebots. There are so many changes that it wouldn't be possible to list them here." diff --git a/html/changelogs/Neerti-RCDs.yml b/html/changelogs/Neerti-RCDs.yml deleted file mode 100644 index 04c5778974..0000000000 --- a/html/changelogs/Neerti-RCDs.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Neerti - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "RCDs can now build grilles and windows, with a new mode. They can also finish walls when used on girders on floor/wall mode." - - rscadd: "Adds various new RCDs that are not obtainable at the moment." diff --git a/html/changelogs/Anewbe - Emitters.yml b/html/changelogs/Poojawa - Sunesoundsntoggles.yml similarity index 90% rename from html/changelogs/Anewbe - Emitters.yml rename to html/changelogs/Poojawa - Sunesoundsntoggles.yml index 1400604d07..ed329d766e 100644 --- a/html/changelogs/Anewbe - Emitters.yml +++ b/html/changelogs/Poojawa - Sunesoundsntoggles.yml @@ -22,7 +22,7 @@ ################################# # Your name. -author: Anewbe +author: Poojawa # Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. delete-after: True @@ -33,4 +33,5 @@ delete-after: True # Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. # Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. changes: - - rscadd: "Emitters can be locked while off, too." + - soundadd: "Added my fancy vore sounds." + - rscadd: "Porting of a few QoL Citadel changes to vore menu related to sounds." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Gibberish.yml b/html/changelogs/Poojawa - devourfeedprefs.yml similarity index 90% rename from html/changelogs/Anewbe - Gibberish.yml rename to html/changelogs/Poojawa - devourfeedprefs.yml index b78f38761e..5fe418cc80 100644 --- a/html/changelogs/Anewbe - Gibberish.yml +++ b/html/changelogs/Poojawa - devourfeedprefs.yml @@ -22,7 +22,7 @@ ################################# # Your name. -author: Anewbe +author: Poojawa # Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. delete-after: True @@ -33,4 +33,5 @@ delete-after: True # Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. # Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. changes: - - tweak: "Adds a Gibberish language that gets used when you goof on a language key." + - rscadd: "Applies Feeding and Devourable prefs." + - bugfix: "Made VoreUI toggles a lot more clear of what mode they're in." \ No newline at end of file diff --git a/html/changelogs/PrismaticGynoid - crashedescapepods.yml b/html/changelogs/PrismaticGynoid - crashedescapepods.yml deleted file mode 100644 index 18d4d3bce2..0000000000 --- a/html/changelogs/PrismaticGynoid - crashedescapepods.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: PrismaticGynoid - -delete-after: True - -changes: - - rscadd: "Adds three new crashed escape pod PoIs." diff --git a/html/changelogs/PrismaticGynoid-somemorepaifixes.yml b/html/changelogs/PrismaticGynoid-somemorepaifixes.yml deleted file mode 100644 index 2173f3cc59..0000000000 --- a/html/changelogs/PrismaticGynoid-somemorepaifixes.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: PrismaticGynoid - -delete-after: True - -changes: - - tweak: "pAIs can now be picked up while unfolded, and can display more than 9 emotions." diff --git a/html/changelogs/TheFurryFeline - Orange_Is_The_New_Orange_Part_2.yml b/html/changelogs/TheFurryFeline - Orange_Is_The_New_Orange_Part_2.yml new file mode 100644 index 0000000000..7d7067ad9d --- /dev/null +++ b/html/changelogs/TheFurryFeline - Orange_Is_The_New_Orange_Part_2.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: TheFurryFeline + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - imageadd: "Add a new sprite for the orange jumpsuit." + - tweak: "Refactors prison jumpsuits into its own thing, change a few files and dmis." \ No newline at end of file diff --git a/html/changelogs/__CHANGELOG_README.txt b/html/changelogs/__CHANGELOG_README.txt new file mode 100644 index 0000000000..166e2bea8b --- /dev/null +++ b/html/changelogs/__CHANGELOG_README.txt @@ -0,0 +1,19 @@ +Changelogs are included with commits as text .yml files created individually by the committer. If you want to create a changelog entry you create a .yml file in the /changelogs directory; nothing else needs to be touched unless you are a maintainer. + +####################################################### + +TO MAKE A CHANGELOG .YML ENTRRY + +1. Make a copy of the file example.yml in html/changelogs and rename it to [YOUR USERNAME]-PR-[YOUR PR NUMBER].yml or [YOUR USERNAME]-[YOUR BRANCH NAME]. Only the username is strictly required, anything else is organizational and can be ignored if you so wish. + +2. Change the author to yourself + +3. Replace the changes text with a description of the changes in your PR, keep the double quotes to avoid errors (your changelog can be written ICly or OOCly, it doesn't matter) + +4. (Optional) set the change prefix (rscadd) to a different one listed above in example.yml (this affects what icon is used for your changelog entry) + +5. When commiting make sure your .yml file is included in the commit (it will usually be unticked as an unversioned file) + +####################################################### + +If you have trouble ask for help in #codershuttle on irc.sorcery.net or read https://tgstation13.org/wiki/Guide_to_Changelogs diff --git a/html/changelogs/arokha - ssao.yml b/html/changelogs/arokha - ssao.yml deleted file mode 100644 index 9dd89cfcef..0000000000 --- a/html/changelogs/arokha - ssao.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Arokha - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Added a 'fake SSAO' toggle to Global in character setup, ported from /tg/. Looks like drop shadows on everything." diff --git a/html/changelogs/ater - rig pressurecooker.yml b/html/changelogs/ater - rig pressurecooker.yml deleted file mode 100644 index 696de0487c..0000000000 --- a/html/changelogs/ater - rig pressurecooker.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Atermonera -delete-after: True -changes - - bugfix: "Rigsuits have been resupplied following a mass-recall after a number of suits were reported to have defective pressure-sealant mechanisms. The new sealant mechanisms have been thoroughly tested and should be less prone to failure." \ No newline at end of file diff --git a/html/changelogs/ater - sinks.yml b/html/changelogs/ater - sinks.yml deleted file mode 100644 index 771921c760..0000000000 --- a/html/changelogs/ater - sinks.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Atermonera -delete-after: True -changes - - bugfix: "Powersink drains have been unclogged and work once more" \ No newline at end of file diff --git a/html/changelogs/ater_botspeak.yml b/html/changelogs/ater_botspeak.yml deleted file mode 100644 index a5ef574c50..0000000000 --- a/html/changelogs/ater_botspeak.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Atermonera -delete-after: True -changes: - - bugfix: "Robot sound files have been decrypted following a bizarre ransomware attack by Boiling Point remnants." diff --git a/html/changelogs/ater_stairs.yml b/html/changelogs/ater_stairs.yml deleted file mode 100644 index fd313ac717..0000000000 --- a/html/changelogs/ater_stairs.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Atermonera -delete-after: True -changes - - bugfix: "Stairs have been straightened and now connect to both the heavens and hells, formerly just the hells." \ No newline at end of file diff --git a/html/changelogs/atermonera_suit_cooling.yml b/html/changelogs/atermonera_suit_cooling.yml deleted file mode 100644 index fce9429e6e..0000000000 --- a/html/changelogs/atermonera_suit_cooling.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: atermonera -delete-after: True -changes: - - bugfix: "Suit coolers and rigsuit coolers now protect FBPs against vacuum again." \ No newline at end of file diff --git a/html/changelogs/chao-chainsawsinmyeyes.yml b/html/changelogs/chao-chainsawsinmyeyes.yml deleted file mode 100644 index be5cc12789..0000000000 --- a/html/changelogs/chao-chainsawsinmyeyes.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Chaoko99 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - tweak: "Added some shading to the trashpile sprrites. diff --git a/html/changelogs/chaoko99 - pullbox.yml b/html/changelogs/chaoko99 - pullbox.yml deleted file mode 100644 index c1bd916206..0000000000 --- a/html/changelogs/chaoko99 - pullbox.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: chaoko99 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Ore bags will automatically pick up items when held, belted, or pocketed, and automatically deposit ore in boxes if one is being pulled." - diff --git a/html/changelogs/chaoko99-OHGODHIDEYOURHUSBANDS.yml b/html/changelogs/chaoko99-OHGODHIDEYOURHUSBANDS.yml deleted file mode 100644 index 95ddeb51e6..0000000000 --- a/html/changelogs/chaoko99-OHGODHIDEYOURHUSBANDS.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Chaoko99 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Prometheans can now wear underwear." diff --git a/html/changelogs/chaoko99-mapmergepy.yml b/html/changelogs/chaoko99-mapmergepy.yml deleted file mode 100644 index 32e38564aa..0000000000 --- a/html/changelogs/chaoko99-mapmergepy.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: chaoko99 - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Added mapmerge PY to the \tools directory of the Vorestation repository." - diff --git a/html/changelogs/kartagrafi-NewHairAndUnderwear.yml b/html/changelogs/kartagrafi-NewHairAndUnderwear.yml deleted file mode 100644 index f28a41c04b..0000000000 --- a/html/changelogs/kartagrafi-NewHairAndUnderwear.yml +++ /dev/null @@ -1,39 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: kartagrafi - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Adds new hairstyle ''Sharp Ponytail''" - - rscadd: "Adds several new underwear - ''Binder'', ''Strapless Binder'', ''Longsleeve Striped Shirt, Pink'', ''Pink Wing Shirt'', ''Pink and Black T-Shirt'', ''Leggings''" - - tweak: "Changes name of ''Supernova'' hairstyle to ''Glossy'', and makes the sprite somewhat neater" - - bugfix: "Fixes a hairstyle not appearing" \ No newline at end of file diff --git a/html/changelogs/kartagrafi-nockrobes.yml b/html/changelogs/kartagrafi-nockrobes.yml deleted file mode 100644 index 3023f38a40..0000000000 --- a/html/changelogs/kartagrafi-nockrobes.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: kartagrafi - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Adds several clothing items to loadout such as: Nock masks, cowls and robes." \ No newline at end of file diff --git a/html/changelogs/neerti-looping_sounds.yml b/html/changelogs/neerti-looping_sounds.yml deleted file mode 100644 index 214ec43678..0000000000 --- a/html/changelogs/neerti-looping_sounds.yml +++ /dev/null @@ -1,38 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Neerti - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - soundadd: "Adds a lot of sounds, and the code to let them loop seemlessly. Things made audible now include the microwave, deep fryer, showers, the supermatter when it's active, the TEGs when active, geiger counters, and severe weather on Sif. The weather has different sounds for when indoors and when outdoors." - - tweak: "The weather sounds, and the supermatter hum can be disabled in your preferences." - - rscadd: "Adds a few more weather types that can only be activated by admin powers, such as ash storms and fallout." diff --git a/html/changelogs/Novacat-PR-5779.yml b/html/changelogs/novacat - teshariplush.yml similarity index 95% rename from html/changelogs/Novacat-PR-5779.yml rename to html/changelogs/novacat - teshariplush.yml index e4cc233b54..2e55ba5a83 100644 --- a/html/changelogs/Novacat-PR-5779.yml +++ b/html/changelogs/novacat - teshariplush.yml @@ -33,4 +33,4 @@ delete-after: True # Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. # Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. changes: - - tweak: "All emergency tanks now spawn at full capacity." + - rscadd: "Adds a teshari plush." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Bugspeak.yml b/html/changelogs/woodrat - Barkitchenmerge.yml similarity index 92% rename from html/changelogs/Anewbe - Bugspeak.yml rename to html/changelogs/woodrat - Barkitchenmerge.yml index 4df4990bdf..f5d874bff5 100644 --- a/html/changelogs/Anewbe - Bugspeak.yml +++ b/html/changelogs/woodrat - Barkitchenmerge.yml @@ -22,7 +22,7 @@ ################################# # Your name. -author: Anewbe +author: Woodrat # Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. delete-after: True @@ -33,4 +33,4 @@ delete-after: True # Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. # Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. changes: - - bugfix: "Vedahq is now properly locked behind a whitelist." + - maptweak: "Rework of the civilian wing, merging the kitchen and the bar into one area." diff --git a/html/changelogs/Woodrat - XenoMove.yml b/html/changelogs/woodrat - mapbugfixes.yml similarity index 87% rename from html/changelogs/Woodrat - XenoMove.yml rename to html/changelogs/woodrat - mapbugfixes.yml index 1173591076..1a27913216 100644 --- a/html/changelogs/Woodrat - XenoMove.yml +++ b/html/changelogs/woodrat - mapbugfixes.yml @@ -33,5 +33,7 @@ delete-after: True # Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. # Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. changes: - - rscadd: "Xenoflora and Xenobio Addition to station, first deck." - - bugfix: "Minor bugfixes including mislabeled lockers in robotics and floor decals." + - maptweak: "Minor movement of the mining vendor." + - maptweak: "Adjustment of the kitchen and bar." + - bugfix: "Area in the coffee shop." + - bugfix: "Missing Ducky in the codelab." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Graves.yml b/html/changelogs/woodrat - mobsizeport.yml similarity index 89% rename from html/changelogs/Anewbe - Graves.yml rename to html/changelogs/woodrat - mobsizeport.yml index fd7b71a680..47acc87c40 100644 --- a/html/changelogs/Anewbe - Graves.yml +++ b/html/changelogs/woodrat - mobsizeport.yml @@ -22,7 +22,7 @@ ################################# # Your name. -author: Anewbe +author: Woodrat # Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. delete-after: True @@ -33,4 +33,5 @@ delete-after: True # Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. # Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. changes: - - rscadd: "Graves are now a thing in the code, will need some testing and probably more work before they get more common." \ No newline at end of file + - rscdel: "Removed the old size modifier traits for mobs." + - rscadd: "Port and tweak of the size modifiers from World Server for mobs." \ No newline at end of file diff --git a/html/changelogs/zaddat.yml b/html/changelogs/zaddat.yml deleted file mode 100644 index 667854662e..0000000000 --- a/html/changelogs/zaddat.yml +++ /dev/null @@ -1,41 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -################################# - -# Your name. -author: Elgeonmb - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - rscadd: "Added the Zaddat species, voidsuit-clad fragile bug people. Whitelist applications will be available at a later date." - - rscadd: "Added glucose hypos to the SWEATMAX vendors and a few other places thoughout the station." - - rscadd: "Zaddat equipment is now available through cargo." - - rscadd: "Some new, very very chuuni accessories to play with; the half cape, the full cape, and the sash" - - tweak: "Most accessories can now fit on suits." - - rscdel: "Dionaea and vox can no longer use station rigs." diff --git a/html/create_object.html b/html/create_object.html index f4e0aa8644..7ed93d9818 100644 --- a/html/create_object.html +++ b/html/create_object.html @@ -26,6 +26,7 @@
        + Type
        Offset: @@ -35,7 +36,7 @@ Number: Dir: - Name:
        + Name:
        Where:

        -
        + Number of matches:
        +

        + +
        + +
        + +