diff --git a/baystation12.dme b/baystation12.dme index 2a699386a9..8731451301 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -679,6 +679,9 @@ #include "code\modules\admin\verbs\possess.dm" #include "code\modules\admin\verbs\pray.dm" #include "code\modules\admin\verbs\randomverbs.dm" +#include "code\modules\admin\verbs\SDQL.dm" +#include "code\modules\admin\verbs\SDQL_2.dm" +#include "code\modules\admin\verbs\SDQL_2_parser.dm" #include "code\modules\admin\verbs\striketeam.dm" #include "code\modules\admin\verbs\striketeam_syndicate.dm" #include "code\modules\admin\verbs\ticklag.dm" @@ -1190,22 +1193,18 @@ #include "code\modules\research\xenoarchaeology\finds\finds_fossils.dm" #include "code\modules\research\xenoarchaeology\finds\finds_misc.dm" #include "code\modules\research\xenoarchaeology\finds\finds_talkingitem.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_accelerator.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_base.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_fourier_transform.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_gas_chromatography.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_hyperspectral.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_ion_mobility.dm" -#include "code\modules\research\xenoarchaeology\machinery\analysis_isotope_ratio.dm" #include "code\modules\research\xenoarchaeology\machinery\artifact_analyser.dm" #include "code\modules\research\xenoarchaeology\machinery\artifact_harvester.dm" #include "code\modules\research\xenoarchaeology\machinery\artifact_scanner.dm" +#include "code\modules\research\xenoarchaeology\machinery\coolant.dm" +#include "code\modules\research\xenoarchaeology\machinery\geosample_scanner.dm" #include "code\modules\research\xenoarchaeology\tools\ano_device_battery.dm" #include "code\modules\research\xenoarchaeology\tools\anomaly_suit.dm" #include "code\modules\research\xenoarchaeology\tools\bunsen_burner.dm" #include "code\modules\research\xenoarchaeology\tools\gearbelt.dm" #include "code\modules\research\xenoarchaeology\tools\suspension_generator.dm" #include "code\modules\research\xenoarchaeology\tools\tools.dm" +#include "code\modules\research\xenoarchaeology\tools\tools_anoscanner.dm" #include "code\modules\research\xenoarchaeology\tools\tools_coresampler.dm" #include "code\modules\research\xenoarchaeology\tools\tools_depthscanner.dm" #include "code\modules\research\xenoarchaeology\tools\tools_locater.dm" @@ -1291,7 +1290,6 @@ #include "code\WorkInProgress\Cael_Aislinn\ShieldGen\shield_gen.dm" #include "code\WorkInProgress\Cael_Aislinn\ShieldGen\shield_gen_external.dm" #include "code\WorkInProgress\Cael_Aislinn\Supermatter\LaserComputer.dm" -#include "code\WorkInProgress\Cael_Aislinn\Supermatter\SuperMatter.dm" #include "code\WorkInProgress\Cael_Aislinn\Supermatter\ZeroPointLaser.dm" #include "code\WorkInProgress\Chinsky\ashtray.dm" #include "code\WorkInProgress\Cib\MedicalSideEffects.dm" @@ -1300,6 +1298,7 @@ #include "code\WorkInProgress\Ported\policetape.dm" #include "code\WorkInProgress\SkyMarshal\officer_stuff.dm" #include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm" +#include "code\WorkInProgress\Yinadele\Supermatter.dm" #include "code\ZAS\Airflow.dm" #include "code\ZAS\Connection.dm" #include "code\ZAS\Debug.dm" diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/fusion_reactions.dm b/code/WorkInProgress/Cael_Aislinn/Rust/fusion_reactions.dm index c083cad713..8293e060f4 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/fusion_reactions.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/fusion_reactions.dm @@ -145,14 +145,14 @@ datum/fusion_reaction/pergium_tritium energy_production = 0 radiation = 5 -datum/fusion_reaction/pergium_deuterium +datum/fusion_reaction/pergium_obdurium primary_reactant = "Pergium" secondary_reactant = "Obdurium" energy_consumption = 5 energy_production = 0 radiation = 5 -datum/fusion_reaction/pergium_tritium +datum/fusion_reaction/pergium_solonium primary_reactant = "Pergium" secondary_reactant = "Solonium" energy_consumption = 5 diff --git a/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm b/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm deleted file mode 100644 index fd3bf8eea6..0000000000 --- a/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm +++ /dev/null @@ -1,186 +0,0 @@ -#define NITROGEN_RETARDATION_FACTOR 12 //Higher == N2 slows reaction more -#define THERMAL_RELEASE_MODIFIER 0.55 //Percentage of output power given to heat generation. - -#define PLASMA_RELEASE_MODIFIER 0.24 //Percentage of output power given to plasma generation. -#define PLASMA_CONVERSION_FACTOR 50 //How much energy per mole of plasma -#define MAX_PLASMA_RELATIVE_INCREASE 0.3 //Percentage of current plasma amounts that can be added to preexisting plasma. - -#define OXYGEN_RELEASE_MODIFIER 0.13 //Percentage of output power given to oxygen generation. -#define OXYGEN_CONVERSION_FACTOR 150 //How much energy per mole of oxygen. -#define MAX_OXYGEN_RELATIVE_INCREASE 0.2 //Percentage of current oxygen amounts that can be added to preexisting oxygen. - -#define RADIATION_POWER_MODIFIER 0.03 //How much power goes to irradiating the area. -#define RADIATION_FACTOR 10 -#define HALLUCINATION_POWER_MODIFIER 0.05 //How much power goes to hallucinations. -#define HALLUCINATION_FACTOR 20 - -#define REACTION_POWER_MODIFIER 4 //Higher == more overall power - -#define WARNING_DELAY 45 //45 seconds between warnings. - -/obj/machinery/power/supermatter - name = "Supermatter" - desc = "A strangely translucent and iridescent crystal. \red You get headaches just from looking at it." - icon = 'icons/obj/engine.dmi' - icon_state = "darkmatter" - density = 1 - anchored = 0 - - var/gasefficency = 0.25 - - var/base_icon_state = "darkmatter" - - var/damage = 0 - var/damage_archived = 0 - var/safe_alert = "Crystaline hyperstructure returning to safe operating levels." - var/warning_point = 100 - var/warning_alert = "Danger! Crystal hyperstructure instability!" - var/emergency_point = 700 - var/emergency_alert = "CRYSTAL DELAMINATION IMMINENT" - var/explosion_point = 1000 - - var/emergency_issued = 0 - - var/explosion_power = 8 - - var/lastwarning = 0 // Time in 1/10th of seconds since the last sent warning - - var/power = 0 - - - shard //Small subtype, less efficient and more sensitive, but less boom. - name = "Supermatter Shard" - desc = "A strangely translucent and iridescent crystal. Looks like it used to be part of a larger structure. \red You get headaches just from looking at it." - icon_state = "darkmatter_shard" - base_icon_state = "darkmatter_shard" - - warning_point = 50 - emergency_point = 500 - explosion_point = 900 - - gasefficency = 0.125 - - explosion_power = 3 //3,6,9,12? Or is that too small? - - - process() - - var/turf/L = loc - - if(!istype(L)) //If we are not on a turf, uh oh. - del src - - //Ok, get the air from the turf - var/datum/gas_mixture/env = L.return_air() - - //Remove gas from surrounding area - var/datum/gas_mixture/removed = env.remove(gasefficency * env.total_moles) - - if (!removed) - return 1 - - if(damage > warning_point) // while the core is still damaged and it's still worth noting its status - if((world.timeofday - lastwarning) / 10 >= WARNING_DELAY) - - if(damage > emergency_point) - //radioalert("states, \"[emergency_alert]\"","Supermatter Monitor") - lastwarning = world.timeofday - else if(damage >= damage_archived) // The damage is still going up - //radioalert("states, \"[warning_alert]\"","Supermatter Monitor") - lastwarning = world.timeofday-150 - else // Phew, we're safe - //radioalert("states, \"[safe_alert]\"","Supermatter Monitor") - lastwarning = world.timeofday - - if(damage > explosion_point) - explosion(loc,explosion_power,explosion_power*2,explosion_power*3,explosion_power*4,1) - del src - - damage_archived = damage - damage = max( damage + ( (removed.temperature - 800) / 150 ) , 0 ) - - if(!removed.total_moles) - damage += max((power-1600)/10,0) - power = max(power,1600) - return 1 - - var/nitrogen_mod = abs((removed.nitrogen / removed.total_moles)) * NITROGEN_RETARDATION_FACTOR - var/oxygen = max(min(removed.oxygen / removed.total_moles - nitrogen_mod, 1), 0) - - var/temp_factor = 0 - if(oxygen > 0.8) - // with a perfect gas mix, make the power less based on heat - temp_factor = 100 - icon_state = "[base_icon_state]_glow" - else - // in normal mode, base the produced energy around the heat - temp_factor = 60 - icon_state = base_icon_state - - //Calculate power released as heat and gas, in as the sqrt of the power. - var/power_factor = (power/500) ** 3 - var/device_energy = oxygen * power_factor - power = max(round((removed.temperature - T0C) / temp_factor) + power - power_factor, 0) //Total laser power plus an overload factor - - //Final energy calcs. - device_energy = max(device_energy * REACTION_POWER_MODIFIER,0) - - //To figure out how much temperature to add each tick, consider that at one atmosphere's worth - //of pure oxygen, with all four lasers firing at standard energy and no N2 present, at room temperature - //that the device energy is around 2140. At that stage, we don't want too much heat to be put out - //Since the core is effectively "cold" - - //Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock - //is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall. - - var/plasma_energy = device_energy * PLASMA_RELEASE_MODIFIER - var/oxygen_energy = device_energy * OXYGEN_RELEASE_MODIFIER - var/other_energy = device_energy * (1- (OXYGEN_RELEASE_MODIFIER + PLASMA_RELEASE_MODIFIER)) - - //Put as much plasma out as is permitted. - if( plasma_energy > removed.total_moles * PLASMA_CONVERSION_FACTOR * MAX_PLASMA_RELATIVE_INCREASE / gasefficency) - removed.toxins += (MAX_PLASMA_RELATIVE_INCREASE * removed.total_moles / gasefficency) - other_energy += plasma_energy - (removed.total_moles * PLASMA_CONVERSION_FACTOR * MAX_PLASMA_RELATIVE_INCREASE / gasefficency) - else - removed.toxins += plasma_energy/PLASMA_CONVERSION_FACTOR - - //Put as much plasma out as is permitted. - if( oxygen_energy > removed.total_moles * OXYGEN_CONVERSION_FACTOR * MAX_OXYGEN_RELATIVE_INCREASE / gasefficency) - removed.oxygen += (MAX_OXYGEN_RELATIVE_INCREASE * removed.total_moles / gasefficency) - other_energy += oxygen_energy - (removed.total_moles * OXYGEN_CONVERSION_FACTOR * MAX_OXYGEN_RELATIVE_INCREASE / gasefficency) - else - removed.oxygen += oxygen_energy/OXYGEN_CONVERSION_FACTOR - - - var/heat_energy = (other_energy*THERMAL_RELEASE_MODIFIER)/(1-(OXYGEN_RELEASE_MODIFIER + PLASMA_RELEASE_MODIFIER)) - var/hallucination_energy = (other_energy*HALLUCINATION_POWER_MODIFIER*HALLUCINATION_FACTOR)/(1-(OXYGEN_RELEASE_MODIFIER + PLASMA_RELEASE_MODIFIER)) - var/rad_energy = (other_energy*RADIATION_POWER_MODIFIER*RADIATION_FACTOR)/(1-(OXYGEN_RELEASE_MODIFIER + PLASMA_RELEASE_MODIFIER)) - - var/heat_applied = max(heat_energy,0) - if(heat_applied + removed.temperature > 800) - removed.temperature = 800 - var/energy_to_reconsider = (heat_applied + removed.temperature - 800) - hallucination_energy += (energy_to_reconsider*HALLUCINATION_POWER_MODIFIER)/(HALLUCINATION_POWER_MODIFIER+RADIATION_POWER_MODIFIER) - rad_energy += (energy_to_reconsider*RADIATION_POWER_MODIFIER)/(HALLUCINATION_POWER_MODIFIER+RADIATION_POWER_MODIFIER) - else - removed.temperature += heat_applied - - removed.update_values() - - env.merge(removed) - - for(var/mob/living/carbon/human/l in view(src, round(hallucination_energy**0.25))) // you have to be seeing the core to get hallucinations - if(prob(10) && !istype(l.glasses, /obj/item/clothing/glasses/meson)) - l.hallucination += hallucination_energy/((get_dist(l,src)**2)) - - for(var/mob/living/l in range(src,round(rad_energy**0.25))) - var/rads = rad_energy/((get_dist(l,src)**2)) - l.apply_effect(rads, IRRADIATE) - - return 1 - - - bullet_act(var/obj/item/projectile/Proj) - if(Proj.flag != "bullet") - power += Proj.damage - return 0 \ No newline at end of file diff --git a/code/WorkInProgress/Yinadele/Supermatter.dm b/code/WorkInProgress/Yinadele/Supermatter.dm new file mode 100644 index 0000000000..b9af01b938 --- /dev/null +++ b/code/WorkInProgress/Yinadele/Supermatter.dm @@ -0,0 +1,257 @@ + +#define NITROGEN_RETARDATION_FACTOR 4 //Higher == N2 slows reaction more +#define THERMAL_RELEASE_MODIFIER 10 //Higher == less heat released during reaction +#define PLASMA_RELEASE_MODIFIER 1500 //Higher == less plasma released by reaction +#define OXYGEN_RELEASE_MODIFIER 750 //Higher == less oxygen released at high temperature/power +#define REACTION_POWER_MODIFIER 1.1 //Higher == more overall power + + +//These would be what you would get at point blank, decreases with distance +#define DETONATION_RADS 200 +#define DETONATION_HALLUCINATION 600 + + +#define WARNING_DELAY 60 //45 seconds between warnings. + +/obj/machinery/power/supermatter + name = "Supermatter" + desc = "A strangely translucent and iridescent crystal. \red You get headaches just from looking at it." + icon = 'icons/obj/engine.dmi' + icon_state = "darkmatter" + density = 1 + anchored = 0 + + var/gasefficency = 0.25 + + var/base_icon_state = "darkmatter" + + var/damage = 0 + var/damage_archived = 0 + var/safe_alert = "Crystaline hyperstructure returning to safe operating levels." + var/warning_point = 100 + var/warning_alert = "Danger! Crystal hyperstructure instability!" + var/emergency_point = 700 + var/emergency_alert = "CRYSTAL DELAMINATION IMMINENT." + var/explosion_point = 1000 + + var/emergency_issued = 0 + + var/explosion_power = 8 + + var/lastwarning = 0 // Time in 1/10th of seconds since the last sent warning + + var/power = 0 + + //Temporary values so that we can optimize this + //How much the bullets damage should be multiplied by when it is added to the internal variables + var/config_bullet_energy = 2 + //How much of the power is left after processing is finished? +// var/config_power_reduction_per_tick = 0.5 + //How much hallucination should it produce per unit of power? + var/config_hallucination_power = 0.1 + + var/obj/item/device/radio/radio + + shard //Small subtype, less efficient and more sensitive, but less boom. + name = "Supermatter Shard" + desc = "A strangely translucent and iridescent crystal that looks like it used to be part of a larger structure. \red You get headaches just from looking at it." + icon_state = "darkmatter_shard" + base_icon_state = "darkmatter_shard" + + warning_point = 50 + emergency_point = 500 + explosion_point = 900 + + gasefficency = 0.125 + + explosion_power = 3 //3,6,9,12? Or is that too small? + + +/obj/machinery/power/supermatter/New() + . = ..() + radio = new (src) + + +/obj/machinery/power/supermatter/Del() + del radio + . = ..() + +/obj/machinery/power/supermatter/proc/explode() + explosion(get_turf(src), explosion_power, explosion_power * 2, explosion_power * 3, explosion_power * 4, 1) + del src + return + +/obj/machinery/power/supermatter/process() + + var/turf/L = loc + + if(!istype(L)) //If we are not on a turf, uh oh. + del src + + //Ok, get the air from the turf + var/datum/gas_mixture/env = L.return_air() + + //Remove gas from surrounding area + var/datum/gas_mixture/removed = env.remove(gasefficency * env.total_moles) + + if(!removed || !removed.total_moles) + damage += max((power-1600)/10, 0) + power = min(power, 1600) + return 1 + + if (!removed) + return 1 + + damage_archived = damage + damage = max( damage + ( (removed.temperature - 800) / 150 ) , 0 ) + + if(damage > warning_point) // while the core is still damaged and it's still worth noting its status + if((world.timeofday - lastwarning) / 10 >= WARNING_DELAY) + + if(damage > emergency_point) + radio.autosay(emergency_alert, "Supermatter Monitor") + lastwarning = world.timeofday + + else if(damage >= damage_archived) // The damage is still going up + radio.autosay(warning_alert, "Supermatter Monitor") + lastwarning = world.timeofday - 150 + + else // Phew, we're safe + radio.autosay(safe_alert, "Supermatter Monitor") + lastwarning = world.timeofday + + if(damage > explosion_point) + for(var/mob/living/mob in living_mob_list) + if(istype(mob, /mob/living/carbon/human)) + //Hilariously enough, running into a closet should make you get hit the hardest. + mob:hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) ) + var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(mob, src) + 1) ) + mob.apply_effect(rads, IRRADIATE) + + explode() + + //Ok, 100% oxygen atmosphere = best reaction + //Maxes out at 100% oxygen pressure + var/oxygen = max(min((removed.oxygen - (removed.nitrogen * NITROGEN_RETARDATION_FACTOR)) / MOLES_CELLSTANDARD, 1), 0) + + var/temp_factor = 100 + + if(oxygen > 0.8) + // with a perfect gas mix, make the power less based on heat + icon_state = "[base_icon_state]_glow" + else + // in normal mode, base the produced energy around the heat + temp_factor = 60 + icon_state = base_icon_state + + power = max( (removed.temperature * temp_factor / T0C) * oxygen + power, 0) //Total laser power plus an overload + + //We've generated power, now let's transfer it to the collectors for storing/usage + transfer_energy() + + var/device_energy = power * REACTION_POWER_MODIFIER + + //To figure out how much temperature to add each tick, consider that at one atmosphere's worth + //of pure oxygen, with all four lasers firing at standard energy and no N2 present, at room temperature + //that the device energy is around 2140. At that stage, we don't want too much heat to be put out + //Since the core is effectively "cold" + + //Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock + //is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall. + removed.temperature += (device_energy / THERMAL_RELEASE_MODIFIER) + + removed.temperature = max(0, min(removed.temperature, 2500)) + + //Calculate how much gas to release + removed.toxins += max(device_energy / PLASMA_RELEASE_MODIFIER, 0) + + removed.oxygen += max((device_energy + removed.temperature - T0C) / OXYGEN_RELEASE_MODIFIER, 0) + + removed.update_values() + + env.merge(removed) + + for(var/mob/living/carbon/human/l in view(src, round(power ** 0.25))) // you have to be seeing the core to get hallucinations + if(!istype(l.glasses, /obj/item/clothing/glasses/meson)) + l.hallucination = max(0, min(200, l.hallucination + power * config_hallucination_power * sqrt( 1 / get_dist(l, src) ) ) ) + + for(var/mob/living/l in range(src, round((power / 100) ** 0.25))) + var/rads = (power / 10) * sqrt( 1 / get_dist(l, src) ) + l.apply_effect(rads, IRRADIATE) + + power -= (power/500)**3 + + return 1 + + +/obj/machinery/power/supermatter/bullet_act(var/obj/item/projectile/Proj) + if(Proj.flag != "bullet") + power += Proj.damage * config_bullet_energy + else + damage += Proj.damage * config_bullet_energy + return 0 + + +/obj/machinery/power/supermatter/attack_paw(mob/user as mob) + return attack_hand(user) + + +/obj/machinery/power/supermatter/attack_robot(mob/user as mob) + return attack_hand(user) + + +/obj/machinery/power/supermatter/attack_hand(mob/user as mob) + user.visible_message("\The [user] reaches out and touches \the [src] inducing a resonance... \his body starts to glow and catch flame before flashing into ash.",\ + "You reach out and touch \the [src], everything starts burning and all you can hear is ringing. Your last thought is \"That was not a wise decision.\"",\ + "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") + + Consume(user) + +/obj/machinery/power/supermatter/proc/transfer_energy() + for(var/obj/machinery/power/rad_collector/R in rad_collectors) + if(get_dist(R, src) <= 15) // Better than using orange() every process + R.receive_pulse(power) + return + +/obj/machinery/power/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) + user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\ + "You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\ + "Everything suddenly goes silent.") + + user.drop_from_inventory(W) + Consume(W) + + user.apply_effect(150, IRRADIATE) + + +/obj/machinery/power/supermatter/Bumped(atom/AM as mob|obj) + if(istype(AM, /mob/living)) + AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... \his body starts to glow and catch flame before flashing into ash.",\ + "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ + "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") + else + AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + + Consume(AM) + + +/obj/machinery/power/supermatter/proc/Consume(var/mob/living/user) + if(istype(user)) + user.dust() + power += 200 + else + del user + + power += 200 + + //Some poor sod got eaten, go ahead and irradiate people nearby. + for(var/mob/living/l in range(10)) + if(l in view()) + l.show_message("As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.", 1,\ + "The unearthly ringing subsides and you notice you have new radiation burns.", 2) + else + l.show_message("You hear an uneartly ringing and notice your skin is covered in fresh radiation burns.", 2) + var/rads = 500 * sqrt( 1 / (get_dist(l, src) + 1) ) + l.apply_effect(rads, IRRADIATE) + diff --git a/code/WorkInProgress/kilakk/fax.dm b/code/WorkInProgress/kilakk/fax.dm index 7ab0440a6b..76bcc0baa4 100644 --- a/code/WorkInProgress/kilakk/fax.dm +++ b/code/WorkInProgress/kilakk/fax.dm @@ -1,19 +1,35 @@ +var/list/obj/machinery/faxmachine/allfaxes = list() +var/list/alldepartments = list("Central Command") + /obj/machinery/faxmachine name = "fax machine" icon = 'icons/obj/library.dmi' icon_state = "fax" - req_access = list(access_lawyer) + req_one_access = list(access_lawyer, access_heads) anchored = 1 density = 1 use_power = 1 idle_power_usage = 30 active_power_usage = 200 power_channel = EQUIP + var/obj/item/weapon/card/id/scan = null // identification var/authenticated = 0 - var/obj/item/weapon/paper/tofax = null // what we're sending to central + + var/obj/item/weapon/paper/tofax = null // what we're sending var/sendcooldown = 0 // to avoid spamming fax messages + var/department = "Unknown" // our department + + var/dpt = "Central Command" // the department we're sending to + +/obj/machinery/faxmachine/New() + ..() + allfaxes += src + + if( !("[department]" in alldepartments) ) + alldepartments += department + /obj/machinery/faxmachine/process() return 0 @@ -26,7 +42,7 @@ /obj/machinery/faxmachine/attack_hand(mob/user as mob) user.set_machine(src) - var/dat = "Central Command Fax Machine
" + var/dat = "Fax Machine
" var/scan_name if(scan) @@ -54,14 +70,15 @@ else dat += "Send
" - dat += "Currently sending: [tofax.name]" + dat += "Currently sending: [tofax.name]
" + dat += "Sending to: [dpt]
" else if(sendcooldown) - dat += "Please insert paper to send to Central Command via secure connection.

" + dat += "Please insert paper to send via secure connection.

" dat += "Transmitter arrays realigning. Please stand by.
" else - dat += "Please insert paper to send to Central Command via secure connection.

" + dat += "Please insert paper to send via secure connection.

" else dat += "Proper authentication is required to use this device.

" @@ -76,10 +93,18 @@ /obj/machinery/faxmachine/Topic(href, href_list) if(href_list["send"]) if(tofax) - Centcomm_fax(tofax.info, tofax.name, usr) - usr << "Message transmitted." - sendcooldown = 1 - spawn(3000) // three minute cooldown. might mess with this number a bit as time goes on + + if(dpt == "Central Command") + Centcomm_fax(tofax.info, tofax.name, usr) + sendcooldown = 1800 + + else + SendFax(tofax.info, tofax.name, usr, dpt) + sendcooldown = 600 + + usr << "Message transmitted successfully." + + spawn(sendcooldown) // cooldown time sendcooldown = 0 if(href_list["remove"]) @@ -107,6 +132,9 @@ scan = I authenticated = 0 + if(href_list["dept"]) + dpt = input(usr, "Which department?", "Choose a department", "") as null|anything in alldepartments + if(href_list["auth"]) if ( (!( authenticated ) && (scan)) ) if (check_access(scan)) @@ -148,3 +176,20 @@ var/msg = "\blue CENTCOMM FAX: [key_name(Sender, 1)] (PP) (VV) (SM) (JMP) (CA) (RPLY): Receiving '[sentname]' via secure connection ... view message" admins << msg + +proc/SendFax(var/sent, var/sentname, var/mob/Sender, var/dpt) + + for(var/obj/machinery/faxmachine/F in allfaxes) + if( F.department == dpt ) + if(! (F.stat & (BROKEN|NOPOWER) ) ) + + flick("faxreceive", F) + + // give the sprite some time to flick + spawn(20) + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( F.loc ) + P.name = "[sentname]" + P.info = "[sent]" + P.update_icon() + + playsound(F.loc, "sound/items/polaroid1.ogg", 50, 1) diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 297bdb890d..664b065d1c 100755 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -456,6 +456,14 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee containername = "fuel tank crate" group = "Engineering" +/datum/supply_packs/coolanttank + name = "Coolant tank crate" + contains = list(/obj/structure/reagent_dispensers/coolanttank) + cost = 16 + containertype = /obj/structure/largecrate + containername = "coolant tank crate" + group = "Medical / Science" + /datum/supply_packs/solar name = "Solar Pack crate" contains = list(/obj/item/solar_assembly, @@ -945,6 +953,25 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee group = "Engineering" access = access_ce +/datum/supply_packs/smbig + name = "Supermatter Core" + contains = list(/obj/machinery/power/supermatter) + cost = 50 + containertype = /obj/structure/closet/crate/secure/plasma + containername = "Supermatter crate (CAUTIION)" + access = access_ce + group = "Engineering" + + +/datum/supply_packs/smsmall + name = "Supermatter Shard" + contains = list(/obj/machinery/power/supermatter/shard) + cost = 25 + containertype = /obj/structure/closet/crate/secure/plasma + containername = "Supermatter crate (CAUTIION)" + access = access_ce + group = "Engineering" + /datum/supply_packs/shield_cap contains = list(/obj/item/weapon/circuitboard/shield_cap) name = "Experimental shield capacitor circuitry" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 6695fe4402..fbf8001483 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -179,18 +179,23 @@ /area/proc/updateicon() if ((fire || eject || party) && ((!requires_power)?(!requires_power):power_environ))//If it doesn't require power, can still activate this proc. if(fire && !eject && !party) - icon_state = "blue" + icon_state = "red" + blend_mode = BLEND_MULTIPLY /*else if(atmosalm && !fire && !eject && !party) icon_state = "bluenew"*/ else if(!fire && eject && !party) icon_state = "red" + blend_mode = BLEND_MULTIPLY else if(party && !fire && !eject) icon_state = "party" + blend_mode = BLEND_MULTIPLY else icon_state = "blue-red" + blend_mode = BLEND_MULTIPLY else // new lighting behaviour with obj lights icon_state = null + blend_mode = BLEND_DEFAULT /* diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index c50767d2f5..f1a0fd2e26 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -26,6 +26,7 @@ supervisors = "your laws and the AI" //Nodrak selection_color = "#ddffdd" minimal_player_age = 21 + alt_titles = list("Android", "Robot") equip(var/mob/living/carbon/human/H) if(!H) return 0 diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 2db4c1594e..477fd67cab 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -28,6 +28,7 @@ var/global/list/autolathe_recipes = list( \ new /obj/item/weapon/surgicaldrill(),\ new /obj/item/weapon/retractor(),\ new /obj/item/weapon/cautery(),\ + new /obj/item/weapon/hemostat(),\ new /obj/item/weapon/reagent_containers/glass/beaker(), \ new /obj/item/weapon/reagent_containers/glass/beaker/large(), \ new /obj/item/weapon/reagent_containers/glass/beaker/vial(), \ diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 8cee9f811c..911ccace95 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -152,7 +152,7 @@ carddesc += "" carddesc += "" carddesc += "Stored account number: " - carddesc += "" + carddesc += "" carddesc += "" carddesc += "Assignment: " diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 1476b13d0a..20415b6721 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -148,6 +148,14 @@ else if(istype(W, /obj/item/weapon/card) && currently_vending) var/obj/item/weapon/card/I = W scan_card(I) + + else if(src.panel_open) + + for(var/datum/data/vending_product/R in product_records) + if(istype(W, R.product_path)) + stock(R, user) + del(W) + else ..() @@ -407,6 +415,13 @@ src.updateUsrDialog() +/obj/machinery/vending/proc/stock(var/datum/data/vending_product/R, var/mob/user) + if(src.panel_open) + user << "\blue You stock the [src] with \a [R.product_name]" + R.amount++ + + src.updateUsrDialog() + /obj/machinery/vending/process() if(stat & (BROKEN|NOPOWER)) return diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index a7e6d57bb3..30fcfbdd49 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -2,6 +2,8 @@ * Contains: * Glass sheets * Reinforced glass sheets + * Plasma Glass Sheets + * Reinforced Plasma Glass Sheets (AKA Holy fuck strong windows) * Glass shards - TODO: Move this into code/game/object/item/weapons */ @@ -15,6 +17,7 @@ icon_state = "sheet-glass" g_amt = 3750 origin_tech = "materials=1" + var/created_window = /obj/structure/window/basic /obj/item/stack/sheet/glass/attack_self(mob/user as mob) @@ -81,9 +84,8 @@ if(!found) dir_to_set = direction break - var/obj/structure/window/W - W = new /obj/structure/window/basic( user.loc, 0 ) + W = new created_window( user.loc, 0 ) W.dir = dir_to_set W.ini_dir = W.dir W.anchored = 0 @@ -98,7 +100,7 @@ user << "\red There is a window in the way." return 1 var/obj/structure/window/W - W = new /obj/structure/window/basic( user.loc, 0 ) + W = new created_window( user.loc, 0 ) W.dir = SOUTHWEST W.ini_dir = SOUTHWEST W.anchored = 0 @@ -293,3 +295,54 @@ H.UpdateDamageIcon() H.updatehealth() ..() + + + + +/* + * Plasma Glass sheets + */ +/obj/item/stack/sheet/glass/plasmaglass + name = "plasma glass" + desc = "A very strong and very resistant sheet of a plasma-glass alloy." + singular_name = "glass sheet" + icon_state = "sheet-plasmaglass" + g_amt = 7500 + origin_tech = "materials=3;plasma=2" + created_window = /obj/structure/window/plasmabasic + +/obj/item/stack/sheet/glass/plasmaglass/attack_self(mob/user as mob) + construct_window(user) + +/obj/item/stack/sheet/glass/plasmaglass/attackby(obj/item/W, mob/user) + ..() + if( istype(W, /obj/item/stack/rods) ) + var/obj/item/stack/rods/V = W + var/obj/item/stack/sheet/glass/plasmarglass/RG = new (user.loc) + RG.add_fingerprint(user) + RG.add_to_stacks(user) + V.use(1) + var/obj/item/stack/sheet/glass/G = src + src = null + var/replace = (user.get_inactive_hand()==G) + G.use(1) + if (!G && !RG && replace) + user.put_in_hands(RG) + else + return ..() + +/* + * Reinforced plasma glass sheets + */ +/obj/item/stack/sheet/glass/plasmarglass + name = "reinforced plasma glass" + desc = "Plasma glass which seems to have rods or something stuck in them." + singular_name = "reinforced plasma glass sheet" + icon_state = "sheet-plasmarglass" + g_amt = 7500 + m_amt = 1875 + origin_tech = "materials=4;plasma=2" + created_window = /obj/structure/window/plasmareinforced + +/obj/item/stack/sheet/glass/plasmarglass/attack_self(mob/user as mob) + construct_window(user) \ No newline at end of file diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index ee6f96161a..937e22975e 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -122,7 +122,8 @@ Step eight: Put in the new, full power cell - if you don't have one, continue with step 15.
Step nine: Quickly put on a Radiation suit.
Step ten: Check if the singularity field generators withstood the down-time - if they didn't, continue with step 15.
- Step eleven: Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering apc is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the apc to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
+ Step eleven: Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering apc is wired to the same powernet, + which the powersink is on, you have to remove the piece of wire which links the apc to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
Step twelve: Grab your crowbar and pry away the tile closest to the APC.
Step thirteen: Use the wirecutters to cut the wire which is conecting the grid to the terminal.
Step fourteen: Go to the bar and tell the guys how you saved them all. Stop reading this guide here.
@@ -189,7 +190,9 @@

How to Clone People

- So there’s 50 dead people lying on the floor, chairs are spinning like no tomorrow and you haven’t the foggiest idea of what to do? Not to worry! This guide is intended to teach you how to clone people and how to do it right, in a simple step-by-step process! If at any point of the guide you have a mental meltdown, genetics probably isn’t for you and you should get a job-change as soon as possible before you’re sued for malpractice. + So there’s 50 dead people lying on the floor, chairs are spinning like no tomorrow and you haven’t the foggiest idea of what to do? Not to worry! + This guide is intended to teach you how to clone people and how to do it right, in a simple step-by-step process! If at any point of the guide you have a mental meltdown, + genetics probably isn’t for you and you should get a job-change as soon as possible before you’re sued for malpractice.
  1. Acquire body
  2. @@ -209,34 +212,48 @@ This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step.

    Step 2: Strip body

    - The cloning machine does not like abiotic items. What this means is you can’t clone anyone if they’re wearing clothes, so take all of it off. If it’s just one person, it’s courteous to put their possessions in the closet. If you have about seven people awaiting cloning, just leave the piles where they are, but don’t mix them around and for God’s sake don’t let people in to steal them. + The cloning machine does not like abiotic items. What this means is you can’t clone anyone if they’re wearing clothes, so take all of it off. If it’s just one person, it’s courteous to put their possessions in the closet. + If you have about seven people awaiting cloning, just leave the piles where they are, but don’t mix them around and for God’s sake don’t let people in to steal them.

    Step 3: Put body in cloning machine

    Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset.

    Step 4: Scan body

    - Go onto the computer and scan the body by pressing ‘Scan - ’. If you’re successful, they will be added to the records (note that this can be done at any time, even with living people, so that they can be cloned without a body in the event that they are lying dead on port solars and didn‘t turn on their suit sensors)! If not, and it says “Error: Mental interface failure.”, then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, click ‘Refresh‘ and try scanning them again. If there’s no success, threaten them with gibbing. Still no success? Skip over to Step 7 and don‘t continue after it, as you have an unresponsive body and it cannot be cloned. If you got “Error: Unable to locate valid genetic data.“, you are trying to clone a monkey - start over. + Go onto the computer and scan the body by pressing ‘Scan - ’. If you’re successful, they will be added to the records (note that this can be done at any time, even with living people, + so that they can be cloned without a body in the event that they are lying dead on port solars and didn‘t turn on their suit sensors)! + If not, and it says “Error: Mental interface failure.”, then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, + click ‘Refresh‘ and try scanning them again. If there’s no success, threaten them with gibbing. + Still no success? Skip over to Step 7 and don‘t continue after it, as you have an unresponsive body and it cannot be cloned. + If you got “Error: Unable to locate valid genetic data.“, you are trying to clone a monkey - start over.

    Step 5: Clone body

    - Now that the body has a record, click ’View Records’, click the subject’s name, and then click ‘Clone’ to start the cloning process. Congratulations! You’re halfway there. Remember not to ‘Eject’ the cloning pod as this will kill the developing clone and you’ll have to start the process again. + Now that the body has a record, click ’View Records’, click the subject’s name, and then click ‘Clone’ to start the cloning process. Congratulations! You’re halfway there. + Remember not to ‘Eject’ the cloning pod as this will kill the developing clone and you’ll have to start the process again.

    Step 6: Get clean SEs for body

    - Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they’re done. If you’re a competent Geneticist, you will already have one ready on your working computer. If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. Go into View/Edit/Transfer Buffer, find an open slot and click “SE“ to save it. Then click ‘Injector’ to get the SEs in syringe form. Put this in your pocket or something for when the body is done. + Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! + For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they’re done. If you’re a competent Geneticist, you will already have one ready on your working computer. + If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. + Go into View/Edit/Transfer Buffer, find an open slot and click “SE“ to save it. Then click ‘Injector’ to get the SEs in syringe form. Put this in your pocket or something for when the body is done.

    Step 7: Put body in morgue

    - Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed “CHEF MEAT” in order to avoid confusion. + Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. + To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed “CHEF MEAT” in order to avoid confusion.

    Step 8: Await cloned body

    Now go back to the lab and wait for your patient to be cloned. It won’t be long now, I promise.

    Step 9: Cyo and clean clean SE injector on person

    - Has your body been cloned yet? Great! As soon as the guy pops out, grab them and stick them in cryo. Clonexadone and Cryoxadone help rebuild their genetic material. Then grab your cleanr SE injector and jab it in them. Once you’ve injected them, they now have clean Structural Enzymes and their defects, if any, will disappear in a short while. + Has your body been cloned yet? Great! As soon as the guy pops out, grab them and stick them in cryo. Clonexadone and Cryoxadone help rebuild their genetic material. Then grab your cleanr SE injector and jab it in them. Once you’ve injected them, + they now have clean Structural Enzymes and their defects, if any, will disappear in a short while.

    Step 10: Give person clothes back

    - Obviously the person will be naked after they have been cloned. Provided you weren’t an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. No matter how cruel you are, it’s simply against protocol to force your patients to walk outside naked. + Obviously the person will be naked after they have been cloned. Provided you weren’t an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. + No matter how cruel you are, it’s simply against protocol to force your patients to walk outside naked.

    Step 11: Send person on their way

    - Give the patient one last check-over - make sure they don’t still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. Once you’re done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance. + Give the patient one last check-over - make sure they don’t still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. + Once you’re done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance.

    If you’ve gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target. @@ -248,7 +265,7 @@ /obj/item/weapon/book/manual/ripley_build_and_repair name = "APLU \"Ripley\" Construction and Operation Manual" icon_state ="book" - author = "Weyland-Yutani Corp" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + author = "Randall Varn, Einstein Engines Senior Mechanic" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned title = "APLU \"Ripley\" Construction and Operation Manual" dat = {" @@ -347,23 +364,38 @@ Those are the basic steps to furthing science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter.

    The R&D Console

    - The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controled. More on those systems in their own sections. On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, because if it gets damaged, you'll lose your data! In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database. + The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controled. More on those systems in their own sections. + On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, + because if it gets damaged, you'll lose your data! + In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. + It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, + upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database. NOTE: The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices.

    Destructive Analyzer

    - This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, you can destroy the object to further those sciences. Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather then futher science. If their reliability is high enough ,it'll also advance their related technologies. + This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, + you can destroy the object to further those sciences. + Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather then futher science. + If their reliability is high enough ,it'll also advance their related technologies.

    Circuit Imprinter

    This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules.

    Protolathe

    - This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid plasma, silver, gold, and diamonds along with a variety of chemicals to produce devices. The downside is that, again, not all devices you make are 100% reliable when you first discover them. + This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid plasma, silver, gold, and diamonds along with a variety of chemicals to produce devices. + The downside is that, again, not all devices you make are 100% reliable when you first discover them.

    Reliability and You

    - As it has been stated, many devices when they're first discovered do not have a 100% reliablity when you first discover them. Instead, the reliablity of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. Once the device reachs a certain minimum reliability, you'll gain tech advancements from it. + As it has been stated, many devices when they're first discovered do not have a 100% reliablity when you first discover them. Instead, + the reliablity of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, + and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. + Once the device reachs a certain minimum reliability, you'll gain tech advancements from it.

    Building a Better Machine

    - Many machines produces from circuit boards and inserted into a machine frame require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. If you use these parts when constructing something, its attributes may be improved. For example, if you use an advanced matter bin when constructing an autolathe (rather then a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine. + Many machines produces from circuit boards and inserted into a machine frame require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. + If you use these parts when constructing something, its attributes may be improved. + For example, if you use an advanced matter bin when constructing an autolathe (rather then a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: + Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine. "} @@ -796,4 +828,170 @@ Disk, Code, Safety, Timer, Disk, RUN!
    Intelligence Analysts believe that normal Nanotrasen procedure is for the Captain to secure the nuclear authorisation disk.
    Good luck! - "} \ No newline at end of file + "} + +/obj/item/weapon/book/manual/atmospipes + name = "Pipes and You: Getting To Know Your Scary Tools" + icon_state = "pipingbook" + author = "Maria Crash, Senior Atmospherics Technician" + title = "Pipes and You: Getting To Know Your Scary Tools" + dat = {" + + + + + + +

    Contents

    +
      +
    1. Author's Forward
    2. +
    3. Basic Piping
    4. +
    5. Insulated Pipes
    6. +
    7. Atmospherics Devices
    8. +
    9. Heat Exchange Systems
    10. +
    11. Final Checks
    12. +
    +

    + +

    HOW TO NOT SUCK QUITE SO HARD AT ATMOSPHERICS


    + Or: What the fuck does a "passive gate" do?

    + + Alright. It has come to my attention that a variety of people are unsure of what a "pipe" is and what it does. + Apparently there is an unnatural fear of these arcane devices and their "gases". Spooky, spooky. So, + this will tell you what every device constructable by an ordinary pipe dispenser within atmospherics actually does. + You are not going to learn what to do with them to be the super best person ever, or how to play guitar with passive gates, + or something like that. Just what stuff does.

    + + +

    Basic Pipes


    + The boring ones.
    + TMost ordinary pipes are pretty straightforward. They hold gas. If gas is moving in a direction for some reason, gas will flow in that direction. + That's about it. Even so, here's all of your wonderful pipe options.
    + +
  3. Straight pipes: They're pipes. One-meter sections. Straight line. Pretty simple. Just about every pipe and device is based around this + standard one-meter size, so most things will take up as much space as one of these.
  4. +
  5. Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
  6. +
  7. Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
  8. +
  9. 4-way manifold: A four-way junction.
  10. +
  11. Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh. Use them to decorate your house or something.
  12. +
  13. Manual Valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
  14. +<
  15. Manual T-Valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


  16. + +

    Insulated Pipes


    + Special Public Service Announcement.
    + Our regular pipes are already insulated. These are completely worthless. Punch anyone who uses them.

    + +

    Devices:


    + They actually do something.
    + This is usually where people get frightened,
    afraid, and start calling on their gods and/or cowering in fear. Yes, I can see you doing that right now. + Stop it. It's unbecoming. Most of these are fairly straightforward.
    + +
  17. Gas Pump: Take a wild guess. It moves gas in the direction it's pointing (marked by the red line on one end). It moves it based on pressure, the maximum output being 4500 kPa (kilopascals). + Ordinary atmospheric pressure, for comparison, is 101.3 kPa, and the minimum pressure of room-temperature pure oxygen needed to not suffocate in a matter of minutes is 16 kPa + (though 18 is preferred using internals, for various reasons).
  18. +
  19. Volume pump: This pump goes based on volume, instead of pressure, and the possible maximum pressure it can create in the pipe on the recieving end is double the gas pump because of this, + clocking in at an incredible 9000 kPa. If a pipe with this is destroyed or damaged, and this pressure of gas escapes, it can be incredibly dangerous depending on the size of the pipe filled. + Don't hook this to the distribution loop, or you will make babies cry and the Chief Engineer brutally beat you.
  20. +
  21. Passive gate: This is essentially a cap on the pressure of gas allowed to flow in a specific direction. + When turned on, instead of actively pumping gas, it measures the pressure flowing through it, and whatever pressure you set is the maximum: it'll cap after that. + In addition, it only lets gas flow one way. The direction the gas flows is opposite the red handle on it, which is confusing to people used to the red stripe on pumps pointing the way.
  22. +
  23. Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
  24. +
  25. Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon". Actvating a panic siphon without very good reason will kill someone. Don't do it.
  26. +
  27. Meter: A little box with some gagues and numbers. Fasten it to any pipe or manifold, and it'll read you the pressure in it. Very useful.
  28. +
  29. Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2", for reference. + Can output this gas at pressures from 0-4500 kPa.
  30. +
  31. Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe, + the rest will continue out the other side. Can also output from 0-4500 kPa.
  32. + +

    Heat Exchange Systems


    + Will not set you on fire.
    + These systems are used to transfer heat only between two pipes. They will not move gases or any other element, but will equalize the temperature (eventually). Note that because of how gases work (remember: pv=nRt), + a higher temperature will raise pressure, and a lower one will lower temperature.
    + +
  33. Pipe: This is a pipe that will exchange heat with the surrounding atmosphere. Place in fire for superheating. Place in space for supercooling.
  34. +
  35. Bent Pipe: Take a wild guess.
  36. +
  37. Junction:Junction:The point where you connect your normal pipes to heat exchange pipes. Not necessary for heat exchangers, but necessary for H/E pipes/bent pipes.
  38. +
  39. Heat Exchanger: These funky-looking bits attach to an open pipe end. Put another heat exchanger directly across from it, and you can transfer heat across two pipes without having to have the gases touch. + This normally shouldn't exchange with the ambient air, despite being totally exposed. Just don't ask questions...

  40. + + + That's about it for pipes. Go forth, armed with this knowledge, and try not to break, burn down, or kill anything. Please.
    + + + + "} + +/obj/item/weapon/book/manual/evaguide + name = "EVA Gear and You: Not Spending All Day Inside" + icon_state = "evabook" + author = "Maria Crash, Senior Atmospherics Technician" + title = "EVA Gear and You: Not Spending All Day Inside" + dat = {" + + + + + + +

    Contents

    +
      +
    1. A forward on using EVA gear
    2. +
    3. Donning a Civilian Suits
    4. +
    5. Putting on a Hardsuit
    6. +
    7. Final Checks
    8. +
    +

    + +

    EVA Gear and You: Not Spending All Day Inside


    + Or: How not to suffocate because there's a hole in your shoes

    + + EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training, + but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into two sections: + Civilian suits and hardsuits.

    + +

    Civilian Suits


    + The bulkiest things this side of Alpha Centauri
    + These suits are the grey ones that are stored in EVA. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiaion or physical impact. + As Medical, Engineering, Security, and Mining all have hardsuits of their own, these don't see much use, but knowing how to put them on is quite useful anyways.

    + + First, take the suit. It should be in three pieces: A top, a bottom,
    and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however, + put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit, + but to an extent they will adjust to you. You can then find the snaps and seals around the waist, where the two pieces meet. Fasten these, and double-check their tightness. + The red indicators around the waist of the lower half will turn green when this is done correctly. Next, put on whatever breathing apparatus you're using, be it a gas mask or a breath mask. Make sure the oxygen tube is fastened into it. + Put on the helmet now, straight forward, and make sure the tube goes into the small opening specifically for internals. Again, fasten seals around the neck, a small indicator light in the inside of the helmet should go from red to off when all is fastened. + There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit, + but it is reccomended to have a full-sized tank on your back for EVA.

    + +

    Hardsuits


    + Heavy, uncomfortable, still the best option.
    + These suits come in Engineering, Mining, and the Armory. There's also a couple Medical Hardsuits in EVA. These provide a lot more protection than the standard suits.

    + + Similarly to the other suits, these are split into three parts. Fastening the pant and top are mostly the same as the other spacesuits, with the exception that these are a bit heavier, + though not as bulky. The helmet goes on differently, with the air tube feeing into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise, + and then is screwed in for one and a quarter full rotations clockwise, leaving the faceplate directly in front of you. There is a small button on the right side of the helmet that activates the helmet light. + The tanks that fasten onto the side slot are emergency tanks, as
    well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.

    + +

    FINAL CHECKS:


    +
  41. Are all seals fastened correctly?
  42. +
  43. Do you either have shoes on under the suit, or magnetic boots on over it?
  44. +
  45. Do you have a mask on and internals on the suit or your back?
  46. +
  47. Do you have a way to communicate with the station in case something goes wrong?
  48. +
  49. Do you have a second person watching if this is a training session?

  50. + + If you don't have any further issues, go out and do whatever is necessary.
    + + + + "} \ No newline at end of file diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index 90da67076b..25f86aed47 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -406,7 +406,6 @@ m_amt = 70 g_amt = 120 origin_tech = "engineering=4;plasma=3" - icon_state = "ewelder" var/last_gen = 0 diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index 54630d3c4a..d1435d383d 100644 --- a/code/game/objects/structures/transit_tubes.dm +++ b/code/game/objects/structures/transit_tubes.dm @@ -99,6 +99,16 @@ obj/structure/ex_act(severity) +/obj/structure/transit_tube/Bumped(mob/AM as mob|obj) + var/obj/structure/transit_tube/T = locate() in AM.loc + if(T) + AM << "The tube's support pylons block your way." + return ..() + else + AM.loc = src.loc + AM << "You slip under the tube." + + /obj/structure/transit_tube/station/New(loc) ..(loc) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 24a9866a54..7d1041aaab 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -11,6 +11,8 @@ var/ini_dir = null var/state = 0 var/reinf = 0 + var/basestate + var/shardtype = /obj/item/weapon/shard // var/silicate = 0 // number of units of silicate // var/icon/silicateIcon = null // the silicated icon @@ -219,11 +221,11 @@ var/index = null index = 0 while(index < 2) - new /obj/item/weapon/shard(loc) + new shardtype(loc) if(reinf) new /obj/item/stack/rods(loc) index++ else - new /obj/item/weapon/shard(loc) + new shardtype(loc) if(reinf) new /obj/item/stack/rods(loc) del(src) return @@ -282,19 +284,9 @@ /obj/structure/window/New(Loc,re=0) ..() - if(re) reinf = re +// if(re) reinf = re ini_dir = dir - if(reinf) - icon_state = "rwindow" - desc = "A reinforced window." - name = "reinforced window" - state = 2*anchored - health = 40 - if(opacity) - icon_state = "twindow" - else - icon_state = "window" update_nearby_tiles(need_rebuild=1) update_nearby_icons() @@ -354,12 +346,12 @@ if(abs(x-W.x)-abs(y-W.y) ) //doesn't count windows, placed diagonally to src junction |= get_dir(src,W) if(opacity) - icon_state = "twindow[junction]" + icon_state = "[basestate][junction]" else if(reinf) - icon_state = "rwindow[junction]" + icon_state = "[basestate][junction]" else - icon_state = "window[junction]" + icon_state = "[basestate][junction]" return @@ -371,18 +363,53 @@ /obj/structure/window/basic + desc = "It looks thin and flimsy. A few knocks with... anything, really should shatter it." icon_state = "window" + basestate = "window" + +/obj/structure/window/plasmabasic + name = "plasma window" + desc = "A plasma-glass alloy window. It looks insanely tough to break. It appears it's also insanely tough to burn through." + basestate = "plasmawindow" + icon_state = "plasmawindow" + shardtype = /obj/item/weapon/shard/plasma + health = 120 + +/obj/structure/window/plasmabasic/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > T0C + 32000) + hit(round(exposed_volume / 1000), 0) + ..() + +/obj/structure/window/plasmareinforced + name = "reinforced plasma window" + desc = "A plasma-glass alloy window, with rods supporting it. It looks hopelessly tough to break. It also looks completely fireproof, considering how basic plasma windows are insanely fireproof." + basestate = "plasmarwindow" + icon_state = "plasmarwindow" + shardtype = /obj/item/weapon/shard/plasma + reinf = 1 + health = 160 + +/obj/structure/window/plasmareinforced/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + return /obj/structure/window/reinforced name = "reinforced window" + desc = "It looks rather strong. Might take a few good hits to shatter it." icon_state = "rwindow" + basestate = "rwindow" + health = 40 reinf = 1 /obj/structure/window/reinforced/tinted name = "tinted window" + desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." icon_state = "twindow" + basestate = "twindow" opacity = 1 /obj/structure/window/reinforced/tinted/frosted name = "frosted window" - icon_state = "fwindow" \ No newline at end of file + desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." + icon_state = "fwindow" + basestate = "fwindow" + health = 30 \ No newline at end of file diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index ccfbd30e1b..1b708d9107 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -46,6 +46,8 @@ var/global/normal_ooc_colour = "#002eb8" display_colour = "#0099cc" //light blue if(holder.rights & R_MOD && !(holder.rights & R_ADMIN)) display_colour = "#184880" //dark blue + if(holder.rights & R_DEBUG && !(holder.rights & R_ADMIN)) + display_colour = "#1b521f" //dark green else if(holder.rights & R_ADMIN) if(config.allow_admin_ooccolor) display_colour = src.prefs.ooccolor diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index dd330ec00c..4795da7353 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -139,7 +139,9 @@ var/list/admin_verbs_debug = list( /client/proc/restart_controller, /client/proc/enable_debug_verbs, /client/proc/callproc, - /client/proc/toggledebuglogs + /client/proc/toggledebuglogs, + /client/proc/SDQL_query, + /client/proc/SDQL2_query ) var/list/admin_verbs_possess = list( /proc/possess, diff --git a/code/modules/admin/verbs/SDQL.dm b/code/modules/admin/verbs/SDQL.dm new file mode 100644 index 0000000000..d8626da403 --- /dev/null +++ b/code/modules/admin/verbs/SDQL.dm @@ -0,0 +1,497 @@ + +//Structured Datum Query Language. Basically SQL meets BYOND objects. + +//Note: For use in BS12, need text_starts_with proc, and to modify the action on select to use BS12's object edit command(s). + +/client/proc/SDQL_query(query_text as message) + set category = "Admin" + if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe. + message_admins("\red ERROR: Non-admin [usr.key] attempted to execute a SDQL query!") + log_admin("Non-admin [usr.key] attempted to execute a SDQL query!") + + var/list/query_list = SDQL_tokenize(query_text) + + if(query_list.len < 2) + if(query_list.len > 0) + usr << "\red SDQL: Too few discrete tokens in query \"[query_text]\". Please check your syntax and try again." + return + + if(!(lowertext(query_list[1]) in list("select", "delete", "update"))) + usr << "\red SDQL: Unknown query type: \"[query_list[1]]\" in query \"[query_text]\". Please check your syntax and try again." + return + + var/list/types = list() + + var/i + for(i = 2; i <= query_list.len; i += 2) + types += query_list[i] + + if(i + 1 >= query_list.len || query_list[i + 1] != ",") + break + + i++ + + var/list/from = list() + + if(i <= query_list.len) + if(lowertext(query_list[i]) in list("from", "in")) + for(i++; i <= query_list.len; i += 2) + from += query_list[i] + + if(i + 1 >= query_list.len || query_list[i + 1] != ",") + break + + i++ + + if(from.len < 1) + from += "world" + + var/list/set_vars = list() + + if(lowertext(query_list[1]) == "update") + if(i <= query_list.len && lowertext(query_list[i]) == "set") + for(i++; i <= query_list.len; i++) + if(i + 2 <= query_list.len && query_list[i + 1] == "=") + set_vars += query_list[i] + set_vars[query_list[i]] = query_list[i + 2] + + else + usr << "\red SDQL: Invalid set parameter in query \"[query_text]\". Please check your syntax and try again." + return + + i += 3 + + if(i >= query_list.len || query_list[i] != ",") + break + + if(set_vars.len < 1) + usr << "\red SDQL: Invalid or missing set in query \"[query_text]\". Please check your syntax and try again." + return + + var/list/where = list() + + if(i <= query_list.len && lowertext(query_list[i]) == "where") + where = query_list.Copy(i + 1) + + var/list/from_objs = list() + if("world" in from) + from_objs += world + else + for(var/f in from) + if(copytext(f, 1, 2) == "'" || copytext(f, 1, 2) == "\"") + from_objs += locate(copytext(f, 2, length(f))) + else if(copytext(f, 1, 2) != "/") + from_objs += locate(f) + else + var/f2 = text2path(f) + if(text_starts_with(f, "/mob")) + for(var/mob/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/turf/space")) + for(var/turf/space/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/turf/simulated")) + for(var/turf/simulated/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/turf/unsimulated")) + for(var/turf/unsimulated/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/turf")) + for(var/turf/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/area")) + for(var/area/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/obj/item")) + for(var/obj/item/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/obj/machinery")) + for(var/obj/machinery/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/obj")) + for(var/obj/m in world) + if(istype(m, f2)) + from_objs += m + + else if(text_starts_with(f, "/atom")) + for(var/atom/m in world) + if(istype(m, f2)) + from_objs += m +/* + else + for(var/datum/m in world) + if(istype(m, f2)) + from_objs += m +*/ + + var/list/objs = list() + + for(var/from_obj in from_objs) + if("*" in types) + objs += from_obj:contents + else + for(var/f in types) + if(copytext(f, 1, 2) == "'" || copytext(f, 1, 2) == "\"") + objs += locate(copytext(f, 2, length(f))) in from_obj + else if(copytext(f, 1, 2) != "/") + objs += locate(f) in from_obj + else + var/f2 = text2path(f) + if(text_starts_with(f, "/mob")) + for(var/mob/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/turf/space")) + for(var/turf/space/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/turf/simulated")) + for(var/turf/simulated/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/turf/unsimulated")) + for(var/turf/unsimulated/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/turf")) + for(var/turf/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/area")) + for(var/area/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/obj/item")) + for(var/obj/item/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/obj/machinery")) + for(var/obj/machinery/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/obj")) + for(var/obj/m in from_obj) + if(istype(m, f2)) + objs += m + + else if(text_starts_with(f, "/atom")) + for(var/atom/m in from_obj) + if(istype(m, f2)) + objs += m + + else + for(var/datum/m in from_obj) + if(istype(m, f2)) + objs += m + + + for(var/datum/t in objs) + var/currently_false = 0 + for(i = 1, i - 1 < where.len, i++) + var/v = where[i++] + var/compare_op = where[i++] + if(!(compare_op in list("==", "=", "<>", "<", ">", "<=", ">=", "!="))) + usr << "\red SDQL: Unknown comparison operator [compare_op] in where clause following [v] in query \"[query_text]\". Please check your syntax and try again." + return + + var/j + for(j = i, j <= where.len, j++) + if(lowertext(where[j]) in list("and", "or", ";")) + break + + if(!currently_false) + var/value = SDQL_text2value(t, v) + var/result = SDQL_evaluate(t, where.Copy(i, j)) + + switch(compare_op) + if("=", "==") + currently_false = !(value == result) + + if("!=", "<>") + currently_false = !(value != result) + + if("<") + currently_false = !(value < result) + + if(">") + currently_false = !(value > result) + + if("<=") + currently_false = !(value <= result) + + if(">=") + currently_false = !(value >= result) + + + if(j > where.len || lowertext(where[j]) == ";") + break + else if(lowertext(where[j]) == "or") + if(currently_false) + currently_false = 0 + else + break + + i = j + + if(currently_false) + objs -= t + + + + usr << "\blue SQDL Query: [query_text]" + message_admins("[usr] executed SDQL query: \"[query_text]\".") +/* + for(var/t in types) + usr << "Type: [t]" + + for(var/t in from) + usr << "From: [t]" + + for(var/t in set_vars) + usr << "Set: [t] = [set_vars[t]]" + + if(where.len) + var/where_str = "" + for(var/t in where) + where_str += "[t] " + + usr << "Where: [where_str]" + + usr << "From objects:" + for(var/datum/t in from_objs) + usr << t + + usr << "Objects:" + for(var/datum/t in objs) + usr << t +*/ + switch(lowertext(query_list[1])) + if("delete") + for(var/datum/t in objs) + del t + + if("update") + for(var/datum/t in objs) + objs[t] = list() + for(var/v in set_vars) + if(v in t.vars) + objs[t][v] = SDQL_text2value(t, set_vars[v]) + + for(var/datum/t in objs) + for(var/v in objs[t]) + t.vars[v] = objs[t][v] + + if("select") + var/text = "" + for(var/datum/t in objs) + if(istype(t, /atom)) + var/atom/a = t + + if(a.x) + text += "\ref[t]: [t] at ([a.x], [a.y], [a.z])
    " + + else if(a.loc && a.loc.x) + text += "\ref[t]: [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
    " + + else + text += "\ref[t]: [t]
    " + + else + text += "\ref[t]: [t]
    " + + //text += "[t]
    " + usr << browse(text, "window=sdql_result") + + +/client/Topic(href,href_list[],hsrc) + if(href_list["SDQL_select"]) + debug_variables(locate(href_list["SDQL_select"])) + + ..() + + +/proc/SDQL_evaluate(datum/object, list/equation) + if(equation.len == 0) + return null + + else if(equation.len == 1) + return SDQL_text2value(object, equation[1]) + + else if(equation[1] == "!") + return !SDQL_evaluate(object, equation.Copy(2)) + + else if(equation[1] == "-") + return -SDQL_evaluate(object, equation.Copy(2)) + + + else + usr << "\red SDQL: Sorry, equations not yet supported :(" + return null + + +/proc/SDQL_text2value(datum/object, text) + if(text2num(text) != null) + return text2num(text) + else if(text == "null") + return null + else if(copytext(text, 1, 2) == "'" || copytext(text, 1, 2) == "\"" ) + return copytext(text, 2, length(text)) + else if(copytext(text, 1, 2) == "/") + return text2path(text) + else + if(findtext(text, ".")) + var/split = findtext(text, ".") + var/v = copytext(text, 1, split) + + if((v in object.vars) && istype(object.vars[v], /datum)) + return SDQL_text2value(object.vars[v], copytext(text, split + 1)) + else + return null + + else + if(text in object.vars) + return object.vars[text] + else + return null + + +/proc/text_starts_with(text, start) + if(copytext(text, 1, length(start) + 1) == start) + return 1 + else + return 0 + + + + + +/proc/SDQL_tokenize(query_text) + + var/list/whitespace = list(" ", "\n", "\t") + var/list/single = list("(", ")", ",", "+", "-") + var/list/multi = list( + "=" = list("", "="), + "<" = list("", "=", ">"), + ">" = list("", "="), + "!" = list("", "=")) + + var/word = "" + var/list/query_list = list() + var/len = length(query_text) + + for(var/i = 1, i <= len, i++) + var/char = copytext(query_text, i, i + 1) + + if(char in whitespace) + if(word != "") + query_list += word + word = "" + + else if(char in single) + if(word != "") + query_list += word + word = "" + + query_list += char + + else if(char in multi) + if(word != "") + query_list += word + word = "" + + var/char2 = copytext(query_text, i + 1, i + 2) + + if(char2 in multi[char]) + query_list += "[char][char2]" + i++ + + else + query_list += char + + else if(char == "'") + if(word != "") + usr << "\red SDQL: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again." + return null + + word = "'" + + for(i++, i <= len, i++) + char = copytext(query_text, i, i + 1) + + if(char == "'") + if(copytext(query_text, i + 1, i + 2) == "'") + word += "'" + i++ + + else + break + + else + word += char + + if(i > len) + usr << "\red SDQL: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again." + return null + + query_list += "[word]'" + word = "" + + else if(char == "\"") + if(word != "") + usr << "\red SDQL: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again." + return null + + word = "\"" + + for(i++, i <= len, i++) + char = copytext(query_text, i, i + 1) + + if(char == "\"") + if(copytext(query_text, i + 1, i + 2) == "'") + word += "\"" + i++ + + else + break + + else + word += char + + if(i > len) + usr << "\red SDQL: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again." + return null + + query_list += "[word]\"" + word = "" + + else + word += char + + if(word != "") + query_list += word + + return query_list diff --git a/code/modules/admin/verbs/SDQL_2.dm b/code/modules/admin/verbs/SDQL_2.dm new file mode 100644 index 0000000000..549447da11 --- /dev/null +++ b/code/modules/admin/verbs/SDQL_2.dm @@ -0,0 +1,426 @@ + + +/client/proc/SDQL2_query(query_text as message) + set category = "Admin" + if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe. + message_admins("\red ERROR: Non-admin [usr.key] attempted to execute a SDQL query!") + log_admin("Non-admin [usr.key] attempted to execute a SDQL query!") + + if(!query_text || length(query_text) < 1) + return + + //world << query_text + + var/list/query_list = SDQL2_tokenize(query_text) + + if(!query_list || query_list.len < 1) + return + + var/list/query_tree = SDQL_parse(query_list) + + if(query_tree.len < 1) + return + + var/list/from_objs = list() + var/list/select_types = list() + + switch(query_tree[1]) + if("explain") + SDQL_testout(query_tree["explain"]) + return + + if("call") + if("on" in query_tree) + select_types = query_tree["on"] + else + return + + if("select", "delete", "update") + select_types = query_tree[query_tree[1]] + + from_objs = SDQL_from_objs(query_tree["from"]) + + var/list/objs = list() + + for(var/type in select_types) + var/char = copytext(type, 1, 2) + + if(char == "/" || char == "*") + for(var/from in from_objs) + objs += SDQL_get_all(type, from) + + else if(char == "'" || char == "\"") + objs += locate(copytext(type, 2, length(type))) + + if("where" in query_tree) + var/objs_temp = objs + objs = list() + for(var/datum/d in objs_temp) + if(SDQL_expression(d, query_tree["where"])) + objs += d + + //usr << "Query: [query_text]" + message_admins("[usr] executed SDQL query: \"[query_text]\".") + + switch(query_tree[1]) + if("delete") + for(var/datum/d in objs) + del d + + if("select") + var/text = "" + for(var/datum/t in objs) + if(istype(t, /atom)) + var/atom/a = t + + if(a.x) + text += "\ref[t]: [t] at ([a.x], [a.y], [a.z])
    " + + else if(a.loc && a.loc.x) + text += "\ref[t]: [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
    " + + else + text += "\ref[t]: [t]
    " + + else + text += "\ref[t]: [t]
    " + + usr << browse(text, "window=SDQL-result") + + if("update") + if("set" in query_tree) + var/list/set_list = query_tree["set"] + for(var/datum/d in objs) + var/list/vals = list() + for(var/v in set_list) + if(v in d.vars) + vals += v + vals[v] = SDQL_expression(d, set_list[v]) + + if(istype(d, /turf)) + for(var/v in vals) + if(v == "x" || v == "y" || v == "z") + continue + + d.vars[v] = vals[v] + + else + for(var/v in vals) + d.vars[v] = vals[v] + + + + + +/proc/SDQL_parse(list/query_list) + var/datum/SDQL_parser/parser = new(query_list) + var/list/query_tree = parser.parse() + + del(parser) + + return query_tree + + + +/proc/SDQL_testout(list/query_tree, indent = 0) + var/spaces = "" + for(var/s = 0, s < indent, s++) + spaces += " " + + for(var/item in query_tree) + if(istype(item, /list)) + world << "[spaces](" + SDQL_testout(item, indent + 1) + world << "[spaces])" + + else + world << "[spaces][item]" + + if(!isnum(item) && query_tree[item]) + + if(istype(query_tree[item], /list)) + world << "[spaces] (" + SDQL_testout(query_tree[item], indent + 2) + world << "[spaces] )" + + else + world << "[spaces] [query_tree[item]]" + + + +/proc/SDQL_from_objs(list/tree) + if("world" in tree) + return list(world) + + var/list/out = list() + + for(var/type in tree) + var/char = copytext(type, 1, 2) + + if(char == "/") + out += SDQL_get_all(type, world) + + else if(char == "'" || char == "\"") + out += locate(copytext(type, 2, length(type))) + + return out + + +/proc/SDQL_get_all(type, location) + var/list/out = list() + + if(type == "*") + for(var/datum/d in location) + out += d + + return out + + type = text2path(type) + + if(ispath(type, /mob)) + for(var/mob/d in location) + if(istype(d, type)) + out += d + + else if(ispath(type, /turf)) + for(var/turf/d in location) + if(istype(d, type)) + out += d + + else if(ispath(type, /obj)) + for(var/obj/d in location) + if(istype(d, type)) + out += d + + else if(ispath(type, /area)) + for(var/area/d in location) + if(istype(d, type)) + out += d + + else if(ispath(type, /atom)) + for(var/atom/d in location) + if(istype(d, type)) + out += d + + else + for(var/datum/d in location) + if(istype(d, type)) + out += d + + return out + + +/proc/SDQL_expression(datum/object, list/expression, start = 1) + var/result = 0 + var/val + + for(var/i = start, i <= expression.len, i++) + var/op = "" + + if(i > start) + op = expression[i] + i++ + + var/list/ret = SDQL_value(object, expression, i) + val = ret["val"] + i = ret["i"] + + if(op != "") + switch(op) + if("+") + result += val + if("-") + result -= val + if("*") + result *= val + if("/") + result /= val + if("&") + result &= val + if("|") + result |= val + if("^") + result ^= val + if("=", "==") + result = (result == val) + if("!=", "<>") + result = (result != val) + if("<") + result = (result < val) + if("<=") + result = (result <= val) + if(">") + result = (result > val) + if(">=") + result = (result >= val) + if("and", "&&") + result = (result && val) + if("or", "||") + result = (result || val) + else + usr << "\red SDQL2: Unknown op [op]" + result = null + else + result = val + + return result + +/proc/SDQL_value(datum/object, list/expression, start = 1) + var/i = start + var/val = null + + if(i > expression.len) + return list("val" = null, "i" = i) + + if(istype(expression[i], /list)) + val = SDQL_expression(object, expression[i]) + + else if(expression[i] == "!") + var/list/ret = SDQL_value(object, expression, i + 1) + val = !ret["val"] + i = ret["i"] + + else if(expression[i] == "~") + var/list/ret = SDQL_value(object, expression, i + 1) + val = ~ret["val"] + i = ret["i"] + + else if(expression[i] == "-") + var/list/ret = SDQL_value(object, expression, i + 1) + val = -ret["val"] + i = ret["i"] + + else if(expression[i] == "null") + val = null + + else if(isnum(expression[i])) + val = expression[i] + + else if(copytext(expression[i], 1, 2) in list("'", "\"")) + val = copytext(expression[i], 2, length(expression[i])) + + else + val = SDQL_var(object, expression, i) + i = expression.len + + return list("val" = val, "i" = i) + +/proc/SDQL_var(datum/object, list/expression, start = 1) + + if(expression[start] in object.vars) + + if(start < expression.len && expression[start + 1] == ".") + return SDQL_var(object.vars[expression[start]], expression[start + 2]) + + else + return object.vars[expression[start]] + + else + return null + +/proc/SDQL2_tokenize(query_text) + + var/list/whitespace = list(" ", "\n", "\t") + var/list/single = list("(", ")", ",", "+", "-", ".") + var/list/multi = list( + "=" = list("", "="), + "<" = list("", "=", ">"), + ">" = list("", "="), + "!" = list("", "=")) + + var/word = "" + var/list/query_list = list() + var/len = length(query_text) + + for(var/i = 1, i <= len, i++) + var/char = copytext(query_text, i, i + 1) + + if(char in whitespace) + if(word != "") + query_list += word + word = "" + + else if(char in single) + if(word != "") + query_list += word + word = "" + + query_list += char + + else if(char in multi) + if(word != "") + query_list += word + word = "" + + var/char2 = copytext(query_text, i + 1, i + 2) + + if(char2 in multi[char]) + query_list += "[char][char2]" + i++ + + else + query_list += char + + else if(char == "'") + if(word != "") + usr << "\red SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again." + return null + + word = "'" + + for(i++, i <= len, i++) + char = copytext(query_text, i, i + 1) + + if(char == "'") + if(copytext(query_text, i + 1, i + 2) == "'") + word += "'" + i++ + + else + break + + else + word += char + + if(i > len) + usr << "\red SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again." + return null + + query_list += "[word]'" + word = "" + + else if(char == "\"") + if(word != "") + usr << "\red SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again." + return null + + word = "\"" + + for(i++, i <= len, i++) + char = copytext(query_text, i, i + 1) + + if(char == "\"") + if(copytext(query_text, i + 1, i + 2) == "'") + word += "\"" + i++ + + else + break + + else + word += char + + if(i > len) + usr << "\red SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again." + return null + + query_list += "[word]\"" + word = "" + + else + word += char + + if(word != "") + query_list += word + + return query_list diff --git a/code/modules/admin/verbs/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL_2_parser.dm new file mode 100644 index 0000000000..d1d30b33b5 --- /dev/null +++ b/code/modules/admin/verbs/SDQL_2_parser.dm @@ -0,0 +1,531 @@ +//I'm pretty sure that this is a recursive [s]descent[/s] ascent parser. + + + +//Spec + +////////// +// +// query : select_query | delete_query | update_query | call_query | explain +// explain : 'EXPLAIN' query +// +// select_query : 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] +// delete_query : 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] +// update_query : 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression] +// call_query : 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]] +// +// select_list : select_item [',' select_list] +// select_item : '*' | select_function | object_type +// select_function : count_function +// count_function : 'COUNT' '(' '*' ')' | 'COUNT' '(' object_types ')' +// +// from_list : from_item [',' from_list] +// from_item : 'world' | object_type +// +// call_function : ['(' [arguments] ')'] +// arguments : expression [',' arguments] +// +// object_type : | string +// +// assignments : assignment, [',' assignments] +// assignment : '=' expression +// variable : | '.' variable +// +// bool_expression : expression comparitor expression [bool_operator bool_expression] +// expression : ( unary_expression | '(' expression ')' | value ) [binary_operator expression] +// unary_expression : unary_operator ( unary_expression | value | '(' expression ')' ) +// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>=' +// value : variable | string | number | 'null' +// unary_operator : '!' | '-' | '~' +// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' +// bool_operator : 'AND' | '&&' | 'OR' | '||' +// +// string : ''' ''' | '"' '"' +// number : +// +////////// + +/datum/SDQL_parser + var/query_type + var/error = 0 + + var/list/query + var/list/tree + + var/list/select_functions = list("count") + var/list/boolean_operators = list("and", "or", "&&", "||") + var/list/unary_operators = list("!", "-", "~") + var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^") + var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=") + + + +/datum/SDQL_parser/New(query_list) + query = query_list + + + +/datum/SDQL_parser/proc/parse_error(error_message) + error = 1 + usr << "\red SQDL2 Parsing Error: [error_message]" + return query.len + 1 + +/datum/SDQL_parser/proc/parse() + tree = list() + query(1, tree) + + if(error) + return list() + else + return tree + +/datum/SDQL_parser/proc/token(i) + if(i <= query.len) + return query[i] + + else + return null + +/datum/SDQL_parser/proc/tokens(i, num) + if(i + num <= query.len) + return query.Copy(i, i + num) + + else + return null + +/datum/SDQL_parser/proc/tokenl(i) + return lowertext(token(i)) + + + +/datum/SDQL_parser/proc + +//query: select_query | delete_query | update_query + query(i, list/node) + query_type = tokenl(i) + + switch(query_type) + if("select") + select_query(i, node) + + if("delete") + delete_query(i, node) + + if("update") + update_query(i, node) + + if("call") + call_query(i, node) + + if("explain") + node += "explain" + node["explain"] = list() + query(i + 1, node["explain"]) + + +// select_query: 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] + select_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) + + node += "select" + node["select"] = select + + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + + node += "from" + node["from"] = from + + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + + node += "where" + node["where"] = where + + return i + + +//delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] + delete_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) + + node += "delete" + node["delete"] = select + + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + + node += "from" + node["from"] = from + + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + + node += "where" + node["where"] = where + + return i + + +//update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression] + update_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) + + node += "update" + node["update"] = select + + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + + node += "from" + node["from"] = from + + if(tokenl(i) != "set") + i = parse_error("UPDATE has misplaced SET") + + var/list/set_assignments = list() + i = assignments(i + 1, set_assignments) + + node += "set" + node["set"] = set_assignments + + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + + node += "where" + node["where"] = where + + return i + + +//call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]] + call_query(i, list/node) + var/list/func = list() + i = call_function(i + 1, func) + + node += "call" + node["call"] = func + + if(tokenl(i) != "on") + return i + + var/list/select = list() + i = select_list(i + 1, select) + + node += "on" + node["on"] = select + + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + + node += "from" + node["from"] = from + + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + + node += "where" + node["where"] = where + + return i + + +//select_list: select_item [',' select_list] + select_list(i, list/node) + i = select_item(i, node) + + if(token(i) == ",") + i = select_list(i + 1, node) + + return i + + +//from_list: from_item [',' from_list] + from_list(i, list/node) + i = from_item(i, node) + + if(token(i) == ",") + i = from_list(i + 1, node) + + return i + + +//assignments: assignment, [',' assignments] + assignments(i, list/node) + i = assignment(i, node) + + if(token(i) == ",") + i = assignments(i + 1, node) + + return i + + +//select_item: '*' | select_function | object_type + select_item(i, list/node) + + if(token(i) == "*") + node += "*" + i++ + + else if(tokenl(i) in select_functions) + i = select_function(i, node) + + else + i = object_type(i, node) + + return i + + +//from_item: 'world' | object_type + from_item(i, list/node) + + if(token(i) == "world") + node += "world" + i++ + + else + i = object_type(i, node) + + return i + + +//bool_expression: expression [bool_operator bool_expression] + bool_expression(i, list/node) + + var/list/bool = list() + i = expression(i, bool) + + node[++node.len] = bool + + if(tokenl(i) in boolean_operators) + i = bool_operator(i, node) + i = bool_expression(i, node) + + return i + + +//assignment: '=' expression + assignment(i, list/node) + + node += token(i) + + if(token(i + 1) == "=") + var/varname = token(i) + node[varname] = list() + + i = expression(i + 2, node[varname]) + + else + parse_error("Assignment expected, but no = found") + + return i + + +//variable: | '.' variable + variable(i, list/node) + var/list/L = list(token(i)) + node[++node.len] = L + + if(token(i + 1) == ".") + L += "." + i = variable(i + 2, L) + + else + i++ + + return i + + +//object_type: | string + object_type(i, list/node) + + if(copytext(token(i), 1, 2) == "/") + node += token(i) + + else + i = string(i, node) + + return i + 1 + + +//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>=' + comparitor(i, list/node) + + if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">=")) + node += token(i) + + else + parse_error("Unknown comparitor [token(i)]") + + return i + 1 + + +//bool_operator: 'AND' | '&&' | 'OR' | '||' + bool_operator(i, list/node) + + if(tokenl(i) in list("and", "or", "&&", "||")) + node += token(i) + + else + parse_error("Unknown comparitor [token(i)]") + + return i + 1 + + +//string: ''' ''' | '"' '"' + string(i, list/node) + + if(copytext(token(i), 1, 2) in list("'", "\"")) + node += token(i) + + else + parse_error("Expected string but found '[token(i)]'") + + return i + 1 + + +//call_function: ['(' [arguments] ')'] + call_function(i, list/node) + + parse_error("Sorry, function calls aren't available yet") + + return i + + +//select_function: count_function + select_function(i, list/node) + + parse_error("Sorry, function calls aren't available yet") + + return i + + +//expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression] + expression(i, list/node) + + if(token(i) in unary_operators) + i = unary_expression(i, node) + + else if(token(i) == "(") + var/list/expr = list() + + i = expression(i + 1, expr) + + if(token(i) != ")") + parse_error("Missing ) at end of expression.") + + else + i++ + + node[++node.len] = expr + + else + i = value(i, node) + + if(token(i) in binary_operators) + i = binary_operator(i, node) + i = expression(i, node) + + else if(token(i) in comparitors) + i = binary_operator(i, node) + + var/list/rhs = list() + i = expression(i, rhs) + + node[++node.len] = rhs + + + return i + + +//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' ) + unary_expression(i, list/node) + + if(token(i) in unary_operators) + var/list/unary_exp = list() + + unary_exp += token(i) + i++ + + if(token(i) in unary_operators) + i = unary_expression(i, unary_exp) + + else if(token(i) == "(") + var/list/expr = list() + + i = expression(i + 1, expr) + + if(token(i) != ")") + parse_error("Missing ) at end of expression.") + + else + i++ + + unary_exp[++unary_exp.len] = expr + + else + i = value(i, unary_exp) + + node[++node.len] = unary_exp + + + else + parse_error("Expected unary operator but found '[token(i)]'") + + return i + + +//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' + binary_operator(i, list/node) + + if(token(i) in (binary_operators + comparitors)) + node += token(i) + + else + parse_error("Unknown binary operator [token(i)]") + + return i + 1 + + +//value: variable | string | number | 'null' + value(i, list/node) + + if(token(i) == "null") + node += "null" + i++ + + else if(isnum(text2num(token(i)))) + node += text2num(token(i)) + i++ + + else if(copytext(token(i), 1, 2) in list("'", "\"")) + i = string(i, node) + + else + i = variable(i, node) + + return i + + + + +/*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/ \ No newline at end of file diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 9db503f93a..2abfe0f4fc 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -251,6 +251,7 @@ 'nano/css/icons.css', 'nano/templates/chem_dispenser.tmpl', 'nano/templates/cryo.tmpl', + 'nano/templates/geoscanner.tmpl', 'nano/templates/dna_modifier.tmpl', 'nano/images/uiBackground.png', 'nano/images/uiIcons16.png', @@ -298,9 +299,5 @@ 'icons/spideros_icons/sos_11.png', 'icons/spideros_icons/sos_12.png', 'icons/spideros_icons/sos_13.png', - 'icons/spideros_icons/sos_14.png', - 'icons/xenoarch_icons/chart1.jpg', - 'icons/xenoarch_icons/chart2.jpg', - 'icons/xenoarch_icons/chart3.jpg', - 'icons/xenoarch_icons/chart4.jpg' + 'icons/spideros_icons/sos_14.png' ) diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 57f18d1e36..b170ea2de6 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -257,15 +257,6 @@ item_state = "neocoat" flags = FPRINT | TABLEPASS -//actual suits - -/obj/item/clothing/suit/creamsuit - name = "cream suit" - desc = "A cream coloured, genteel suit." - icon_state = "creamsuit" - item_state = "creamsuit" - flags = FPRINT | TABLEPASS - //stripper /obj/item/clothing/under/stripper/stripper_pink diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 382f4c2d10..98331e8fcc 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -478,6 +478,7 @@ siemens_coefficient = 0.30 permeability_coefficient = 0.01 item_color="white" + species_restricted = list("exclude","Unathi") /obj/item/clothing/gloves/fluff/walter_brooks_1 //botanistpower: Walter Brooks name = "mittens" @@ -860,6 +861,7 @@ desc = "Made of a special fiber that gives special protection against biohazards. Has a cross on the chest denoting that the wearer is trained medical personnel and short sleeves." icon = 'icons/obj/custom_items.dmi' icon_state = "medical_short" + item_state = "medical_short" item_color = "medical_short" /obj/item/clothing/suit/storage/labcoat/fluff/red diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 34319c0d91..54f63f9f9f 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -99,8 +99,9 @@ new /obj/item/weapon/book/manual/engineering_particle_accelerator(src) new /obj/item/weapon/book/manual/engineering_hacking(src) new /obj/item/weapon/book/manual/engineering_guide(src) + new /obj/item/weapon/book/manual/atmospipes(src) new /obj/item/weapon/book/manual/engineering_singularity_safety(src) - new /obj/item/weapon/book/manual/robotics_cyborgs(src) + new /obj/item/weapon/book/manual/evaguide(src) update_icon() /obj/structure/bookcase/manuals/research_and_development diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index ac206cf1b8..313ae1cf1b 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -298,9 +298,25 @@ new /obj/item/stack/sheet/mineral/clown(output.loc) else on = 0 + if (selected_glass == 1 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 1 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0) + if (ore_glass > 0 && ore_plasma > 0) + ore_glass--; + ore_plasma--; + new /obj/item/stack/sheet/glass/plasmaglass(output.loc) + else + on = 0 + if (selected_glass == 1 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 1 && selected_uranium == 0 && selected_iron == 1 && selected_clown == 0) + if (ore_glass > 0 && ore_plasma > 0 && ore_iron > 0) + ore_glass--; + ore_iron--; + ore_plasma--; + new /obj/item/stack/sheet/glass/plasmarglass(output.loc) + else + on = 0 continue //THESE TWO ARE CODED FOR URIST TO USE WHEN HE GETS AROUND TO IT. //They were coded on 18 Feb 2012. If you're reading this in 2015, then firstly congratulations on the world not ending on 21 Dec 2012 and secondly, Urist is apparently VERY lazy. ~Errorage + //Iamgoofball here, this comment I'm typing right now was made in 11/1/2013. If you're reading this in 2020, then please let me know if the world has gone into a nuclear apocalypse. Also Urist has been tried and hung for how lazy he was. That and he was jaywalking. /*if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 1 && selected_plasma == 0 && selected_uranium == 1 && selected_iron == 0 && selected_clown == 0) if (ore_uranium >= 2 && ore_diamond >= 1) ore_uranium -= 2 @@ -380,34 +396,42 @@ if (O) if (istype(O,/obj/item/weapon/ore/iron)) ore_iron++; + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/glass)) ore_glass++; + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/diamond)) ore_diamond++; + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/plasma)) ore_plasma++ + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/gold)) ore_gold++ + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/silver)) ore_silver++ + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/uranium)) ore_uranium++ + O.loc = null del(O) continue if (istype(O,/obj/item/weapon/ore/clown)) ore_clown++ + O.loc = null del(O) continue O.loc = src.output.loc diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 46a37dae54..ccdcc56e64 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -42,6 +42,10 @@ dat += text("Reinforced Glass: [machine.ore_rglass] Release
    ") if(machine.ore_plasma) dat += text("Plasma: [machine.ore_plasma] Release
    ") + if(machine.ore_plasmaglass) + dat += text("Plasma Glass: [machine.ore_plasmaglass] Release
    ") + if(machine.ore_plasmarglass) + dat += text("Reinforced Plasma Glass: [machine.ore_plasmarglass] Release
    ") if(machine.ore_gold) dat += text("Gold: [machine.ore_gold] Release
    ") if(machine.ore_silver) @@ -83,6 +87,18 @@ G.amount = machine.ore_plasma G.loc = machine.output.loc machine.ore_plasma = 0 + if ("plasmaglass") + if (machine.ore_plasmaglass > 0) + var/obj/item/stack/sheet/glass/plasmaglass/G = new /obj/item/stack/sheet/glass/plasmaglass + G.amount = machine.ore_plasmaglass + G.loc = machine.output.loc + machine.ore_plasmaglass = 0 + if ("plasmarglass") + if (machine.ore_plasmarglass > 0) + var/obj/item/stack/sheet/glass/plasmarglass/G = new /obj/item/stack/sheet/glass/plasmarglass + G.amount = machine.ore_plasmarglass + G.loc = machine.output.loc + machine.ore_plasmarglass = 0 if ("uranium") if (machine.ore_uranium > 0) var/obj/item/stack/sheet/mineral/uranium/G = new /obj/item/stack/sheet/mineral/uranium @@ -195,6 +211,8 @@ var/ore_silver = 0; var/ore_diamond = 0; var/ore_plasma = 0; + var/ore_plasmaglass = 0; + var/ore_plasmarglass = 0; var/ore_iron = 0; var/ore_uranium = 0; var/ore_clown = 0; @@ -263,6 +281,14 @@ ore_rglass+= O:amount del(O) continue + if (istype(O,/obj/item/stack/sheet/glass/plasmaglass)) + ore_plasmaglass+= O:amount + del(O) + continue + if (istype(O,/obj/item/stack/sheet/glass/plasmarglass)) + ore_plasmarglass+= O:amount + del(O) + continue if (istype(O,/obj/item/stack/sheet/plasteel)) ore_plasteel+= O:amount del(O) @@ -349,6 +375,18 @@ G.loc = output.loc ore_rglass -= stack_amt return + if (ore_plasmaglass >= stack_amt) + var/obj/item/stack/sheet/glass/plasmaglass/G = new /obj/item/stack/sheet/glass/plasmaglass + G.amount = stack_amt + G.loc = output.loc + ore_plasmaglass -= stack_amt + return + if (ore_plasmarglass >= stack_amt) + var/obj/item/stack/sheet/glass/plasmarglass/G = new /obj/item/stack/sheet/glass/plasmarglass + G.amount = stack_amt + G.loc = output.loc + ore_plasmarglass -= stack_amt + return if (ore_plasteel >= stack_amt) var/obj/item/stack/sheet/plasteel/G = new /obj/item/stack/sheet/plasteel G.amount = stack_amt diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index 0eb859083a..83077154a8 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -4,6 +4,8 @@ #define XENOARCH_SPREAD_CHANCE 15 #define ARTIFACT_SPAWN_CHANCE 20 +/datum/controller/game_controller/var/list/artifact_spawning_turfs = list() + /turf/simulated/mineral //wall piece name = "Rock" icon = 'icons/turf/walls.dmi' @@ -127,7 +129,7 @@ //dont create artifact machinery in animal or plant digsites, or if we already have one if(!artifact_find && digsite != 1 && digsite != 2 && prob(ARTIFACT_SPAWN_CHANCE)) artifact_find = new() - artifact_spawning_turfs.Add(src) + master_controller.artifact_spawning_turfs.Add(src) if(!src.geological_data) src.geological_data = new/datum/geosample(src) @@ -369,29 +371,25 @@ commented out in r5061, I left it because of the shroom thingies //just pull the surrounding rock out excavate_find(0, F) - if( src.excavation_level + P.excavation_amount >= 100 || (!finds.len && !excavation_minerals.len) ) - //if players have been excavating this turf, have a chance to leave some rocky debris behind - var/boulder_prob = 0 + if( src.excavation_level + P.excavation_amount >= 100 ) + //if players have been excavating this turf, leave some rocky debris behind var/obj/structure/boulder/B - - if(src.excavation_level > 15) - boulder_prob = 10 if(artifact_find) - boulder_prob += 25 - if(src.excavation_level >= 100) - boulder_prob += 40 - else if(src.excavation_level > 95) - boulder_prob += 25 - else if(src.excavation_level > 90) - boulder_prob += 10 - if(prob(boulder_prob)) + if( src.excavation_level > 0 || prob(15) ) + //boulder with an artifact inside + B = new(src) + if(artifact_find) + B.artifact_find = artifact_find + else + artifact_debris(1) + else if(prob(15)) + //empty boulder B = new(src) - if(artifact_find) - B.artifact_find = artifact_find - else if(artifact_find && src.excavation_level + P.excavation_amount >= 100) - artifact_debris(1) - gets_drilled(B ? 0 : 1) + if(B) + gets_drilled(0) + else + gets_drilled(1) return else src.excavation_level += P.excavation_amount @@ -475,7 +473,7 @@ commented out in r5061, I left it because of the shroom thingies if(prob(50)) pain = 1 for(var/mob/living/M in range(src, 200)) - M << "[pick("A high pitched [pick("keening","wailing","whistle")]","A rumbling noise like [pick("thunder","heavy machinery")]")] somehow penetrates your mind before fadaing away!" + M << "[pick("A high pitched [pick("keening","wailing","whistle")]","A rumbling noise like [pick("thunder","heavy machinery")]")] somehow penetrates your mind before fading away!" if(pain) flick("pain",M.pain) if(prob(50)) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 4ca6efea54..6b597920ff 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -180,7 +180,7 @@ This function restores the subjects blood to max. /mob/living/carbon/human/proc/restore_blood() var/blood_volume = vessel.get_reagent_amount("blood") vessel.add_reagent("blood",560.0-blood_volume) - + /* This function restores all organs. @@ -252,8 +252,9 @@ This function restores all organs. W.loc = src else if(istype(used_weapon,/obj/item/projectile)) //We don't want to use the actual projectile item, so we spawn some shrapnel. - if(prob(75) && damagetype == BRUTE) - var/obj/item/projectile/P = used_weapon + + var/obj/item/projectile/P = used_weapon + if(prob(75) && P.embed) var/obj/item/weapon/shard/shrapnel/S = new() S.name = "[P.name] shrapnel" S.desc = "[S.desc] It looks like it was fired from [P.shot_from]." diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 7f34b503b2..0c822a4fa3 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1433,7 +1433,7 @@ else if(health < config.health_threshold_softcrit) shock_stage = max(shock_stage, 61) else - shock_stage = min(shock_stage, 100) + shock_stage = min(shock_stage, 160) shock_stage = max(shock_stage-1, 0) return @@ -1450,15 +1450,26 @@ if (shock_stage >= 60) if(shock_stage == 60) emote("me",1,"'s body becomes limp.") + if (prob(2)) + src << ""+pick("The pain is excrutiating!", "Please, just end the pain!", "Your whole body is going numb!") + Weaken(20) + + if(shock_stage >= 80) if (prob(5)) - Stun(20) - lying = 1 + src << ""+pick("The pain is excrutiating!", "Please, just end the pain!", "Your whole body is going numb!") + Weaken(20) - if(shock_stage == 80) - src << ""+pick("You see a light at the end of the tunnel!", "You feel like you could die any moment now.", "You're about to lose consciousness.") + if(shock_stage >= 120) + if (prob(2)) + src << ""+pick("You black out!", "You feel like you could die any moment now.", "You're about to lose consciousness.") + Paralyse(5) - if(shock_stage > 80) - Paralyse(rand(15,28)) + if(shock_stage == 150) + emote("me",1,"can no longer stand, collapsing!") + Weaken(20) + + if(shock_stage >= 150) + Weaken(20) proc/handle_pulse() if(life_tick % 5) return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) diff --git a/code/modules/mob/living/carbon/shock.dm b/code/modules/mob/living/carbon/shock.dm index fd2356be26..0c1ba44207 100644 --- a/code/modules/mob/living/carbon/shock.dm +++ b/code/modules/mob/living/carbon/shock.dm @@ -5,11 +5,11 @@ /mob/living/carbon/proc/updateshock() src.traumatic_shock = \ 1 * src.getOxyLoss() + \ - 0.5 * src.getToxLoss() + \ - 2.5 * src.getFireLoss() + \ - 1.5 * src.getBruteLoss() + \ - 2 * src.getCloneLoss() + \ - 1 * src.halloss + 0.7 * src.getToxLoss() + \ + 1.5 * src.getFireLoss() + \ + 1.2 * src.getBruteLoss() + \ + 1.7 * src.getCloneLoss() + \ + 2 * src.halloss if(reagents.has_reagent("alkysine")) src.traumatic_shock -= 10 @@ -35,7 +35,7 @@ else if(organ.status & ORGAN_BROKEN || organ.open) src.traumatic_shock += 30 if(organ.status & ORGAN_SPLINTED) - src.traumatic_shock -= 20 + src.traumatic_shock -= 25 if(src.traumatic_shock < 0) src.traumatic_shock = 0 diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index bb5ed93e2a..0015620cc5 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -171,9 +171,14 @@ O.loc = loc O.job = "Cyborg" - - O.mmi = new /obj/item/device/mmi(O) - O.mmi.transfer_identity(src)//Does not transfer key/client. + if(O.mind.assigned_role == "Cyborg") + if(O.mind.role_alt_title == "Android") + O.mmi = new /obj/item/device/mmi/posibrain(O) + if(O.mind.role_alt_title == "Robot") + O.mmi = new /obj/item/device/mmi/posibrain(O) //Ravensdale wants a circuit based brain for another robot class, this is a placeholder. + else + O.mmi = new /obj/item/device/mmi(O) + O.mmi.transfer_identity(src)//Does not transfer key/client. O.Namepick() diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 38e4e47307..7658aa303a 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -890,17 +890,17 @@ obj/item/weapon/organ/head/attackby(obj/item/weapon/W as obj, mob/user as mob) switch(brain_op_stage) if(0) for(var/mob/O in (oviewers(brainmob) - user)) - O.show_message("\red [brainmob] is beginning to have \his head cut open with [src] by [user].", 1) - brainmob << "\red [user] begins to cut open your head with [src]!" - user << "\red You cut [brainmob]'s head open with [src]!" + O.show_message("\red [brainmob] is beginning to have \his head cut open with [W] by [user].", 1) + brainmob << "\red [user] begins to cut open your head with [W]!" + user << "\red You cut [brainmob]'s head open with [W]!" brain_op_stage = 1 if(2) for(var/mob/O in (oviewers(brainmob) - user)) - O.show_message("\red [brainmob] is having \his connections to the brain delicately severed with [src] by [user].", 1) - brainmob << "\red [user] begins to cut open your head with [src]!" - user << "\red You cut [brainmob]'s head open with [src]!" + O.show_message("\red [brainmob] is having \his connections to the brain delicately severed with [W] by [user].", 1) + brainmob << "\red [user] begins to cut open your head with [W]!" + user << "\red You cut [brainmob]'s head open with [W]!" brain_op_stage = 3.0 else @@ -909,20 +909,20 @@ obj/item/weapon/organ/head/attackby(obj/item/weapon/W as obj, mob/user as mob) switch(brain_op_stage) if(1) for(var/mob/O in (oviewers(brainmob) - user)) - O.show_message("\red [brainmob] has \his skull sawed open with [src] by [user].", 1) - brainmob << "\red [user] begins to saw open your head with [src]!" - user << "\red You saw [brainmob]'s head open with [src]!" + O.show_message("\red [brainmob] has \his skull sawed open with [W] by [user].", 1) + brainmob << "\red [user] begins to saw open your head with [W]!" + user << "\red You saw [brainmob]'s head open with [W]!" brain_op_stage = 2 if(3) for(var/mob/O in (oviewers(brainmob) - user)) - O.show_message("\red [brainmob] has \his spine's connection to the brain severed with [src] by [user].", 1) - brainmob << "\red [user] severs your brain's connection to the spine with [src]!" - user << "\red You sever [brainmob]'s brain's connection to the spine with [src]!" + O.show_message("\red [brainmob] has \his spine's connection to the brain severed with [W] by [user].", 1) + brainmob << "\red [user] severs your brain's connection to the spine with [W]!" + user << "\red You sever [brainmob]'s brain's connection to the spine with [W]!" - user.attack_log += "\[[time_stamp()]\] Debrained [brainmob.name] ([brainmob.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])" - brainmob.attack_log += "\[[time_stamp()]\] Debrained by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])" - msg_admin_attack("[brainmob] ([brainmob.ckey]) debrained [user] ([user.ckey]) (INTENT: [uppertext(user.a_intent)]) (JMP)") + user.attack_log += "\[[time_stamp()]\] Debrained [brainmob.name] ([brainmob.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)])" + brainmob.attack_log += "\[[time_stamp()]\] Debrained by [user.name] ([user.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)])" + msg_admin_attack("[user] ([user.ckey]) debrained [brainmob] ([brainmob.ckey]) (INTENT: [uppertext(user.a_intent)]) (JMP)") var/obj/item/brain/B = new(loc) B.transfer_identity(brainmob) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7bcfecfa46..13c1a0888f 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -136,6 +136,14 @@ in_chamber.current = curloc in_chamber.yo = targloc.y - curloc.y in_chamber.xo = targloc.x - curloc.x + if(istype(user, /mob/living/carbon)) + var/mob/living/carbon/mob = user + if(mob.shock_stage > 120) + in_chamber.yo += rand(-2,2) + in_chamber.xo += rand(-2,2) + else if(mob.shock_stage > 70) + in_chamber.yo += rand(-1,1) + in_chamber.xo += rand(-1,1) if(params) var/list/mouse_control = params2list(params) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 9dbcabc47d..97fa5b1dbd 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -50,6 +50,7 @@ var/drowsy = 0 var/agony = 0 + var/embed = 0 // whether or not the projectile can embed itself in the mob proc/on_hit(var/atom/target, var/blocked = 0) if(blocked >= 2) return 0//Full block diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 36e456285a..a88381b2a0 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -5,16 +5,18 @@ damage_type = BRUTE nodamage = 0 flag = "bullet" + embed = 1 on_hit(var/atom/target, var/blocked = 0) if (..(target, blocked)) var/mob/living/L = target shake_camera(L, 3, 2) -/obj/item/projectile/bullet/weakbullet +/obj/item/projectile/bullet/weakbullet // "rubber" bullets damage = 10 stun = 5 weaken = 5 + embed = 0 /obj/item/projectile/bullet/midbullet @@ -48,6 +50,7 @@ stun = 10 weaken = 10 stutter = 10 + embed = 0 /obj/item/projectile/bullet/a762 damage = 25 \ No newline at end of file diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index 364ad53370..2fab1a4da1 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -4,6 +4,7 @@ icon_state = "ice_1" damage = 20 flag = "energy" + embed = 1 /obj/item/projectile/forcebolt/strong name = "force bolt" diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 8b5400fa40..a42b3c1d9b 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -15,9 +15,12 @@ var/amount = 30 var/beaker = null var/recharged = 0 + var/hackedcheck = 0 var/list/dispensable_reagents = list("hydrogen","lithium","carbon","nitrogen","oxygen","fluorine", "sodium","aluminum","silicon","phosphorus","sulfur","chlorine","potassium","iron", "copper","mercury","radium","water","ethanol","sugar","sacid","tungsten") + var/list/broken_requirements = list() + var/broken_on_spawn = 0 /obj/machinery/chem_dispenser/proc/recharge() if(stat & (BROKEN|NOPOWER)) return @@ -49,6 +52,28 @@ recharge() dispensable_reagents = sortList(dispensable_reagents) + if(broken_on_spawn) + var/amount = pick(1,2,2,3,4) + var/list/options = list() + options[/obj/item/weapon/stock_parts/capacitor/adv] = "Add an advanced capacitor to fix it." + options[/obj/item/weapon/stock_parts/console_screen] = "Replace the console screen to fix it." + options[/obj/item/weapon/stock_parts/manipulator/pico] = "Upgrade to a pico manipulator to fix it." + options[/obj/item/weapon/stock_parts/matter_bin/adv] = "Give it an advanced matter bin to fix it." + options[/obj/item/stack/sheet/mineral/diamond] = "Line up a cut diamond with the nozzle to fix it." + options[/obj/item/stack/sheet/mineral/uranium] = "Position a uranium sheet inside to fix it." + options[/obj/item/stack/sheet/mineral/plasma] = "Enter a block of plasma to fix it." + options[/obj/item/stack/sheet/mineral/silver] = "Cover the internals with a silver lining to fix it." + options[/obj/item/stack/sheet/mineral/gold] = "Wire a golden filament to fix it." + options[/obj/item/stack/sheet/plasteel] = "Surround the outside with a plasteel cover to fix it." + options[/obj/item/stack/sheet/rglass] = "Insert a pane of reinforced glass to fix it." + + while(amount > 0) + amount -= 1 + + var/index = pick(options) + broken_requirements[index] = options[index] + options -= index + /obj/machinery/chem_dispenser/ex_act(severity) switch(severity) if(1.0) @@ -74,14 +99,17 @@ * * @param user /mob The mob who is interacting with this ui * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui * * @return nothing */ -/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) +/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main") if(stat & (BROKEN|NOPOWER)) return if(user.stat || user.restrained()) return + if(broken_requirements.len) + user << "[src] is broken. [broken_requirements[broken_requirements[1]]]" + return + // this is the data which will be sent to the ui var/data[0] data["amount"] = amount @@ -111,13 +139,10 @@ chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("dispense" = temp.id)))) // list in a list because Byond merges the first list... data["chemicals"] = chemicals - if (!ui) // no ui has been passed, so we'll search for one - { - ui = nanomanager.get_open_ui(user, src, ui_key) - } + var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, ui_key) if (!ui) // the ui does not exist, so we'll create a new one - ui = new(user, src, ui_key, "chem_dispenser.tmpl", "Chem Dispenser 5000", 374, 640) + ui = new(user, src, ui_key, "chem_dispenser.tmpl", "Chem Dispenser 5000", 370, 605) // When the UI is first opened this is the data it will use ui.set_initial_data(data) ui.open() @@ -159,18 +184,26 @@ if(isrobot(user)) return - if(!istype(B, /obj/item/weapon/reagent_containers/glass)) + if(broken_requirements.len && B.type == broken_requirements[1]) + broken_requirements -= broken_requirements[1] + user << "You fix [src]." + if(istype(B,/obj/item/stack)) + var/obj/item/stack/S = B + S.use(1) + else + user.drop_item() + del(B) return - if(src.beaker) - user << "A beaker is already loaded into the machine." + user << "Something is already loaded into the machine." + return + if(istype(B, /obj/item/weapon/reagent_containers/glass||/obj/item/weapon/reagent_containers/food)) + src.beaker = B + user.drop_item() + B.loc = src + user << "You set [B] on the machine." + nanomanager.update_uis(src) // update all UIs attached to src return - - src.beaker = B - user.drop_item() - B.loc = src - user << "You add the beaker to the machine!" - nanomanager.update_uis(src) // update all UIs attached to src /obj/machinery/chem_dispenser/attack_ai(mob/user as mob) return src.attack_hand(user) @@ -184,6 +217,50 @@ ui_interact(user) +/obj/machinery/chem_dispenser/soda + icon_state = "soda_dispenser" + name = "soda fountain" + desc = "A drink fabricating machine, capable of producing many sugary drinks with just one touch." + energy = 100 + max_energy = 100 + dispensable_reagents = list("water","ice","coffee","tea","icetea","space_cola","spacemountainwind","dr_gibb","space_up","tonic","sodawater","lemon_lime","sugar","orangejuice","limejuice") + + /obj/machinery/chem_dispenser/soda/attackby(var/obj/item/weapon/B as obj, var/mob/user as mob) + ..() + if(istype(B, /obj/item/device/multitool)) + if(hackedcheck == 0) + user << "You change the mode from 'McNano' to 'Pizza King'." + dispensable_reagents += list("thirteenloko","grapesoda") + hackedcheck = 1 + return + + else + user << "You change the mode from 'Pizza King' to 'McNano'." + dispensable_reagents -= list("thirteenloko") + hackedcheck = 0 + return +/obj/machinery/chem_dispenser/beer + icon_state = "booze_dispenser" + name = "booze dispenser" + energy = 100 + max_energy = 100 + desc = "A technological marvel, supposedly able to mix just the mixture you'd like to drink the moment you ask for one." + dispensable_reagents = list("water","ice","coffee","tea","cream","lemon_lime","sugar","orangejuice","limejuice","cola","sodawater","tonic","beer","kahlua","whiskey","wine","vodka","gin","rum","tequila","vermouth","cognac","ale","mead") + + /obj/machinery/chem_dispenser/beer/attackby(var/obj/item/weapon/B as obj, var/mob/user as mob) + ..() + if(istype(B, /obj/item/device/multitool)) + if(hackedcheck == 0) + user << "You disable the 'nanotrasen-are-cheap-bastards' lock, enabling hidden and very expensive boozes." + dispensable_reagents += list("goldschlager","patron","watermelonjuice","berryjuice") + hackedcheck = 1 + return + + else + user << "You re-enable the 'nanotrasen-are-cheap-bastards' lock, disabling hidden and very expensive boozes." + dispensable_reagents -= list("goldschlager","patron","watermelonjuice","berryjuice") + hackedcheck = 0 + return ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -347,15 +424,15 @@ reagents.clear_reagents() icon_state = "mixer0" else if (href_list["createpill"] || href_list["createpill_multiple"]) + var/name = reject_bad_text(input(usr,"Name:","Name your pill!",reagents.get_master_reagent_name())) var/count = 1 if (href_list["createpill_multiple"]) count = isgoodnumber(input("Select the number of pills to make.", 10, pillamount) as num) if (count > 20) count = 20 //Pevent people from creating huge stacks of pills easily. Maybe move the number to defines? var/amount_per_pill = reagents.total_volume/count if (amount_per_pill > 50) amount_per_pill = 50 - var/name = reject_bad_text(input(usr,"Name:","Name your pill!","[reagents.get_master_reagent_name()] ([amount_per_pill] units)")) while (count--) var/obj/item/weapon/reagent_containers/pill/P = new/obj/item/weapon/reagent_containers/pill(src.loc) - if(!name) name = "[reagents.get_master_reagent_name()] ([amount_per_pill] units)" + if(!name) name = reagents.get_master_reagent_name() P.name = "[name] pill" P.pixel_x = rand(-7, 7) //random position P.pixel_y = rand(-7, 7) @@ -367,9 +444,9 @@ src.updateUsrDialog() else if (href_list["createbottle"]) if(!condi) - var/name = reject_bad_text(input(usr,"Name:","Name your bottle!","[reagents.get_master_reagent_name()] ([reagents.total_volume] units)")) + var/name = reject_bad_text(input(usr,"Name:","Name your bottle!",reagents.get_master_reagent_name())) var/obj/item/weapon/reagent_containers/glass/bottle/P = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc) - if(!name) name = "[reagents.get_master_reagent_name()] ([reagents.total_volume] units)" + if(!name) name = reagents.get_master_reagent_name() P.name = "[name] bottle" P.pixel_x = rand(-7, 7) //random position P.pixel_y = rand(-7, 7) diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index d760a8e555..beb594d9bc 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -2347,6 +2347,13 @@ datum id = "grapejuice" description = "It's grrrrrape!" color = "#863333" // rgb: 134, 51, 51 + + drink/grapesoda + name = "Grape Soda" + id = "grapesoda" + description = "Grapes made into a fine drank." + color = "#421C52" // rgb: 98, 57, 53 + adj_drowsy = -3 drink/poisonberryjuice name = "Poison Berry Juice" diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 07c9883f2e..3d97068901 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -1790,6 +1790,14 @@ datum result = "barefoot" required_reagents = list("berryjuice" = 1, "cream" = 1, "vermouth" = 1) result_amount = 3 + + grapesoda //Allows Grape Soda to be made + name = "Grape Soda" + id = "grapesoda" + result = "grapesoda" + required_reagents = list("grapejuice" = 2, "cola" = 1) + result_amount = 3 + ////DRINKS THAT REQUIRED IMPROVED SPRITES BELOW:: -Agouri///// diff --git a/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm b/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm index 2aec56dc0a..2c08c1ce60 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm @@ -1,5 +1,5 @@ - - + + /obj/item/weapon/reagent_containers/food/drinks/drinkingglass name = "glass" desc = "Your standard drinking glass." @@ -434,6 +434,10 @@ icon_state = "grapejuice" name = "Glass of grape juice" desc = "It's grrrrrape!" + if("grapesoda") + icon_state = "grapesoda" + name = "Can of Grape Soda" + desc = "Looks like a delicious drank!" if("icetea") icon_state = "icedteaglass" name = "Iced Tea" @@ -499,4 +503,4 @@ New() ..() reagents.add_reagent("cola", 50) - on_reagent_change() \ No newline at end of file + on_reagent_change() diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index ad68c27b67..b4a7ecf209 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -1,318 +1,337 @@ - -//////////////////////////////////////////////////////////////////////////////// -/// (Mixing)Glass. -//////////////////////////////////////////////////////////////////////////////// -/obj/item/weapon/reagent_containers/glass - name = " " - var/base_name = " " - desc = " " - icon = 'icons/obj/chemical.dmi' - icon_state = "null" - item_state = "null" - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(5,10,15,25,30,50) - volume = 50 - flags = FPRINT | TABLEPASS | OPENCONTAINER - - var/label_text = "" - - var/list/can_be_placed_into = list( - /obj/machinery/chem_master/, - /obj/machinery/chem_dispenser/, - /obj/machinery/reagentgrinder, - /obj/structure/table, - /obj/structure/closet, - /obj/structure/sink, - /obj/item/weapon/storage, - /obj/machinery/atmospherics/unary/cryo_cell, - /obj/machinery/dna_scannernew, - /obj/item/weapon/grenade/chem_grenade, - /obj/machinery/bot/medbot, - /obj/machinery/computer/pandemic, - /obj/item/weapon/storage/secure/safe, - /obj/machinery/iv_drip, - /obj/machinery/disease2/incubator, - /obj/machinery/disposal, - /obj/machinery/apiary, - /mob/living/simple_animal/cow, - /mob/living/simple_animal/hostile/retaliate/goat, + +//////////////////////////////////////////////////////////////////////////////// +/// (Mixing)Glass. +//////////////////////////////////////////////////////////////////////////////// +/obj/item/weapon/reagent_containers/glass + name = " " + var/base_name = " " + desc = " " + icon = 'icons/obj/chemical.dmi' + icon_state = "null" + item_state = "null" + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(5,10,15,25,30,50) + volume = 50 + flags = FPRINT | TABLEPASS | OPENCONTAINER + + var/label_text = "" + + var/list/can_be_placed_into = list( + /obj/machinery/chem_master/, + /obj/machinery/chem_dispenser/, + /obj/machinery/reagentgrinder, + /obj/structure/table, + /obj/structure/closet, + /obj/structure/sink, + /obj/item/weapon/storage, + /obj/machinery/atmospherics/unary/cryo_cell, + /obj/machinery/dna_scannernew, + /obj/item/weapon/grenade/chem_grenade, + /obj/machinery/bot/medbot, + /obj/machinery/computer/pandemic, + /obj/item/weapon/storage/secure/safe, + /obj/machinery/iv_drip, + /obj/machinery/disease2/incubator, + /obj/machinery/disposal, + /obj/machinery/apiary, + /mob/living/simple_animal/cow, + /mob/living/simple_animal/hostile/retaliate/goat, /obj/machinery/computer/centrifuge, /obj/machinery/sleeper ) - - New() - ..() - base_name = name - - examine() - set src in view() - ..() - if (!(usr in view(2)) && usr!=src.loc) return - usr << "\blue It contains:" - if(reagents && reagents.reagent_list.len) - usr << "\blue [src.reagents.total_volume] units of liquid." - else - usr << "\blue Nothing." - if (!is_open_container()) - usr << "\blue Airtight lid seals it completely." - - attack_self() - ..() - if (is_open_container()) - usr << "You put the lid on \the [src]." - flags ^= OPENCONTAINER - else - usr << "You take the lid off \the [src]." - flags |= OPENCONTAINER - update_icon() - - afterattack(obj/target, mob/user, proximity) - if(!proximity) return - if (!is_open_container()) - return - - for(var/type in src.can_be_placed_into) - if(istype(target, type)) - return - - if(ismob(target) && target.reagents && reagents.total_volume) - user << "\blue You splash the solution onto [target]." - - var/mob/living/M = target - var/list/injected = list() - for(var/datum/reagent/R in src.reagents.reagent_list) - injected += R.name - var/contained = english_list(injected) - M.attack_log += text("\[[time_stamp()]\] Has been splashed with [src.name] by [user.name] ([user.ckey]). Reagents: [contained]") - user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to splash [M.name] ([M.key]). Reagents: [contained]") - msg_admin_attack("[user.name] ([user.ckey]) splashed [M.name] ([M.key]) with [src.name]. Reagents: [contained] (INTENT: [uppertext(user.a_intent)]) (JMP)") - - for(var/mob/O in viewers(world.view, user)) - O.show_message(text("\red [] has been splashed with something by []!", target, user), 1) - src.reagents.reaction(target, TOUCH) - spawn(5) src.reagents.clear_reagents() - return - else if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us. - - if(!target.reagents.total_volume && target.reagents) - user << "\red [target] is empty." - return - - if(reagents.total_volume >= reagents.maximum_volume) - user << "\red [src] is full." - return - - var/trans = target.reagents.trans_to(src, target:amount_per_transfer_from_this) - user << "\blue You fill [src] with [trans] units of the contents of [target]." - - else if(target.is_open_container() && target.reagents) //Something like a glass. Player probably wants to transfer TO it. - if(!reagents.total_volume) - user << "\red [src] is empty." - return - - if(target.reagents.total_volume >= target.reagents.maximum_volume) - user << "\red [target] is full." - return - - var/trans = src.reagents.trans_to(target, amount_per_transfer_from_this) - user << "\blue You transfer [trans] units of the solution to [target]." - - //Safety for dumping stuff into a ninja suit. It handles everything through attackby() and this is unnecessary. - else if(istype(target, /obj/item/clothing/suit/space/space_ninja)) - return - - else if(istype(target, /obj/machinery/bunsen_burner)) - return - - else if(istype(target, /obj/machinery/anomaly)) - return - - else if(reagents.total_volume) - user << "\blue You splash the solution onto [target]." - src.reagents.reaction(target, TOUCH) - spawn(5) src.reagents.clear_reagents() - return - - 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)) - var/tmp_label = sanitize(input(user, "Enter a label for [src.name]","Label",src.label_text)) - if(length(tmp_label) > 10) - user << "\red The label can be at most 10 characters long." - else - user << "\blue You set the label to \"[tmp_label]\"." - src.label_text = tmp_label - src.update_name_label() - - proc/update_name_label() - if(src.label_text == "") - src.name = src.base_name - else - src.name = "[src.base_name] ([src.label_text])" - -/obj/item/weapon/reagent_containers/glass/beaker - name = "beaker" - desc = "A beaker. Can hold up to 50 units." - icon = 'icons/obj/chemical.dmi' - icon_state = "beaker" - item_state = "beaker" - m_amt = 0 - g_amt = 500 - - on_reagent_change() - update_icon() - - pickup(mob/user) - ..() - update_icon() - - dropped(mob/user) - ..() - update_icon() - - attack_hand() - ..() - update_icon() - - update_icon() - overlays.Cut() - - if(reagents.total_volume) - var/image/filling = image('icons/obj/reagentfillings.dmi', src, "[icon_state]10") - - var/percent = round((reagents.total_volume / volume) * 100) - switch(percent) - if(0 to 9) filling.icon_state = "[icon_state]-10" - if(10 to 24) filling.icon_state = "[icon_state]10" - if(25 to 49) filling.icon_state = "[icon_state]25" - if(50 to 74) filling.icon_state = "[icon_state]50" - if(75 to 79) filling.icon_state = "[icon_state]75" - if(80 to 90) filling.icon_state = "[icon_state]80" - if(91 to INFINITY) filling.icon_state = "[icon_state]100" - - filling.icon += mix_color_from_reagents(reagents.reagent_list) - overlays += filling - - if (!is_open_container()) - var/image/lid = image(icon, src, "lid_[initial(icon_state)]") - overlays += lid - -/obj/item/weapon/reagent_containers/glass/beaker/large - name = "large beaker" - desc = "A large beaker. Can hold up to 100 units." - icon_state = "beakerlarge" - g_amt = 5000 - volume = 100 - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(5,10,15,25,30,50,100) - flags = FPRINT | TABLEPASS | OPENCONTAINER - -/obj/item/weapon/reagent_containers/glass/beaker/vial - name = "vial" - desc = "A small glass vial. Can hold up to 25 units." - icon_state = "vial" - g_amt = 250 - volume = 25 - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(5,10,15,25) - flags = FPRINT | TABLEPASS | OPENCONTAINER - -/obj/item/weapon/reagent_containers/glass/beaker/cryoxadone - New() - ..() - reagents.add_reagent("cryoxadone", 30) - update_icon() - -/obj/item/weapon/reagent_containers/glass/beaker/sulphuric - New() - ..() - reagents.add_reagent("sacid", 50) - update_icon() - -/obj/item/weapon/reagent_containers/glass/beaker/slime - New() - ..() - reagents.add_reagent("slimejelly", 50) - update_icon() - -/obj/item/weapon/reagent_containers/glass/bucket - desc = "It's a bucket." - name = "bucket" - icon = 'icons/obj/janitor.dmi' - icon_state = "bucket" - item_state = "bucket" - m_amt = 200 - g_amt = 0 - w_class = 3.0 - amount_per_transfer_from_this = 20 - possible_transfer_amounts = list(10,20,30,50,70) - volume = 70 - flags = FPRINT | OPENCONTAINER - - attackby(var/obj/D, mob/user as mob) - if(isprox(D)) - user << "You add [D] to [src]." - del(D) - user.put_in_hands(new /obj/item/weapon/bucket_sensor) - user.drop_from_inventory(src) - del(src) - -// vials are defined twice, what? -/* -/obj/item/weapon/reagent_containers/glass/beaker/vial - name = "vial" - desc = "Small glass vial. Looks fragile." - icon_state = "vial" - g_amt = 500 - volume = 15 - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list(1,5,15) - flags = FPRINT | TABLEPASS | OPENCONTAINER */ - -/* -/obj/item/weapon/reagent_containers/glass/blender_jug - name = "Blender Jug" - desc = "A blender jug, part of a blender." - icon = 'icons/obj/kitchen.dmi' - icon_state = "blender_jug_e" - volume = 100 - - on_reagent_change() - switch(src.reagents.total_volume) - if(0) - icon_state = "blender_jug_e" - if(1 to 75) - icon_state = "blender_jug_h" - if(76 to 100) - icon_state = "blender_jug_f" - -/obj/item/weapon/reagent_containers/glass/canister //not used apparantly - desc = "It's a canister. Mainly used for transporting fuel." - name = "canister" - icon = 'icons/obj/tank.dmi' - icon_state = "canister" - item_state = "canister" - m_amt = 300 - g_amt = 0 - w_class = 4.0 - - amount_per_transfer_from_this = 20 - possible_transfer_amounts = list(10,20,30,60) - volume = 120 - flags = FPRINT - -/obj/item/weapon/reagent_containers/glass/dispenser - name = "reagent glass" - desc = "A reagent glass." - icon = 'icons/obj/chemical.dmi' - icon_state = "beaker0" - amount_per_transfer_from_this = 10 - flags = FPRINT | TABLEPASS | OPENCONTAINER - -/obj/item/weapon/reagent_containers/glass/dispenser/surfactant - name = "reagent glass (surfactant)" - icon_state = "liquid" - - New() - ..() - reagents.add_reagent("fluorosurfactant", 20) - -*/ + + New() + ..() + base_name = name + + examine() + set src in view() + ..() + if (!(usr in view(2)) && usr!=src.loc) return + usr << "\blue It contains:" + if(reagents && reagents.reagent_list.len) + usr << "\blue [src.reagents.total_volume] units of liquid." + else + usr << "\blue Nothing." + if (!is_open_container()) + usr << "\blue Airtight lid seals it completely." + + attack_self() + ..() + if (is_open_container()) + usr << "You put the lid on \the [src]." + flags ^= OPENCONTAINER + else + usr << "You take the lid off \the [src]." + flags |= OPENCONTAINER + update_icon() + + afterattack(obj/target, mob/user , flag) + if (!is_open_container()) + return + + for(var/type in src.can_be_placed_into) + if(istype(target, type)) + return + + if(ismob(target) && target.reagents && reagents.total_volume) + user << "\blue You splash the solution onto [target]." + + var/mob/living/M = target + var/list/injected = list() + for(var/datum/reagent/R in src.reagents.reagent_list) + injected += R.name + var/contained = english_list(injected) + M.attack_log += text("\[[time_stamp()]\] Has been splashed with [src.name] by [user.name] ([user.ckey]). Reagents: [contained]") + user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to splash [M.name] ([M.key]). Reagents: [contained]") + msg_admin_attack("[user.name] ([user.ckey]) splashed [M.name] ([M.key]) with [src.name]. Reagents: [contained] (INTENT: [uppertext(user.a_intent)]) (JMP)") + + for(var/mob/O in viewers(world.view, user)) + O.show_message(text("\red [] has been splashed with something by []!", target, user), 1) + src.reagents.reaction(target, TOUCH) + spawn(5) src.reagents.clear_reagents() + return + else if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us. + + if(!target.reagents.total_volume && target.reagents) + user << "\red [target] is empty." + return + + if(reagents.total_volume >= reagents.maximum_volume) + user << "\red [src] is full." + return + + var/trans = target.reagents.trans_to(src, target:amount_per_transfer_from_this) + user << "\blue You fill [src] with [trans] units of the contents of [target]." + + else if(target.is_open_container() && target.reagents) //Something like a glass. Player probably wants to transfer TO it. + if(!reagents.total_volume) + user << "\red [src] is empty." + return + + if(target.reagents.total_volume >= target.reagents.maximum_volume) + user << "\red [target] is full." + return + + var/trans = src.reagents.trans_to(target, amount_per_transfer_from_this) + user << "\blue You transfer [trans] units of the solution to [target]." + + //Safety for dumping stuff into a ninja suit. It handles everything through attackby() and this is unnecessary. + else if(istype(target, /obj/item/clothing/suit/space/space_ninja)) + return + + else if(istype(target, /obj/machinery/bunsen_burner)) + return + + else if(istype(target, /obj/machinery/radiocarbon_spectrometer)) + return + + else if(reagents.total_volume) + user << "\blue You splash the solution onto [target]." + src.reagents.reaction(target, TOUCH) + spawn(5) src.reagents.clear_reagents() + return + + 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)) + var/tmp_label = sanitize(input(user, "Enter a label for [src.name]","Label",src.label_text)) + if(length(tmp_label) > 10) + user << "\red The label can be at most 10 characters long." + else + user << "\blue You set the label to \"[tmp_label]\"." + src.label_text = tmp_label + src.update_name_label() + + proc/update_name_label() + if(src.label_text == "") + src.name = src.base_name + else + src.name = "[src.base_name] ([src.label_text])" + +/obj/item/weapon/reagent_containers/glass/beaker + name = "beaker" + desc = "A beaker. Can hold up to 50 units." + icon = 'icons/obj/chemical.dmi' + icon_state = "beaker" + item_state = "beaker" + m_amt = 0 + g_amt = 500 + + on_reagent_change() + update_icon() + + pickup(mob/user) + ..() + update_icon() + + dropped(mob/user) + ..() + update_icon() + + attack_hand() + ..() + update_icon() + + update_icon() + overlays.Cut() + + if(reagents.total_volume) + var/image/filling = image('icons/obj/reagentfillings.dmi', src, "[icon_state]10") + + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 9) filling.icon_state = "[icon_state]-10" + if(10 to 24) filling.icon_state = "[icon_state]10" + if(25 to 49) filling.icon_state = "[icon_state]25" + if(50 to 74) filling.icon_state = "[icon_state]50" + if(75 to 79) filling.icon_state = "[icon_state]75" + if(80 to 90) filling.icon_state = "[icon_state]80" + if(91 to INFINITY) filling.icon_state = "[icon_state]100" + + filling.icon += mix_color_from_reagents(reagents.reagent_list) + overlays += filling + + if (!is_open_container()) + var/image/lid = image(icon, src, "lid_[initial(icon_state)]") + overlays += lid + +/obj/item/weapon/reagent_containers/glass/beaker/large + name = "large beaker" + desc = "A large beaker. Can hold up to 100 units." + icon_state = "beakerlarge" + g_amt = 5000 + volume = 100 + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(5,10,15,25,30,50,100) + flags = FPRINT | TABLEPASS | OPENCONTAINER + +/obj/item/weapon/reagent_containers/glass/beaker/noreact + name = "cryostasis beaker" + desc = "A cryostasis beaker that allows for chemical storage without reactions. Can hold up to 50 units." + icon_state = "beakernoreact" + g_amt = 500 + volume = 50 + amount_per_transfer_from_this = 10 + flags = FPRINT | TABLEPASS | OPENCONTAINER | NOREACT + +/obj/item/weapon/reagent_containers/glass/beaker/bluespace + name = "bluespace beaker" + desc = "A bluespace beaker, powered by experimental bluespace technology and Element Cuban combined with the Compound Pete. Can hold up to 300 units." + icon_state = "beakerbluespace" + g_amt = 5000 + volume = 300 + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(5,10,15,25,30,50,100,300) + flags = FPRINT | TABLEPASS | OPENCONTAINER + + +/obj/item/weapon/reagent_containers/glass/beaker/vial + name = "vial" + desc = "A small glass vial. Can hold up to 25 units." + icon_state = "vial" + g_amt = 250 + volume = 25 + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(5,10,15,25) + flags = FPRINT | TABLEPASS | OPENCONTAINER + +/obj/item/weapon/reagent_containers/glass/beaker/cryoxadone + New() + ..() + reagents.add_reagent("cryoxadone", 30) + update_icon() + +/obj/item/weapon/reagent_containers/glass/beaker/sulphuric + New() + ..() + reagents.add_reagent("sacid", 50) + update_icon() + +/obj/item/weapon/reagent_containers/glass/beaker/slime + New() + ..() + reagents.add_reagent("slimejelly", 50) + update_icon() + +/obj/item/weapon/reagent_containers/glass/bucket + desc = "It's a bucket." + name = "bucket" + icon = 'icons/obj/janitor.dmi' + icon_state = "bucket" + item_state = "bucket" + m_amt = 200 + g_amt = 0 + w_class = 3.0 + amount_per_transfer_from_this = 20 + possible_transfer_amounts = list(10,20,30,50,70) + volume = 70 + flags = FPRINT | OPENCONTAINER + + attackby(var/obj/D, mob/user as mob) + if(isprox(D)) + user << "You add [D] to [src]." + del(D) + user.put_in_hands(new /obj/item/weapon/bucket_sensor) + user.drop_from_inventory(src) + del(src) + +// vials are defined twice, what? +/* +/obj/item/weapon/reagent_containers/glass/beaker/vial + name = "vial" + desc = "Small glass vial. Looks fragile." + icon_state = "vial" + g_amt = 500 + volume = 15 + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list(1,5,15) + flags = FPRINT | TABLEPASS | OPENCONTAINER */ + +/* +/obj/item/weapon/reagent_containers/glass/blender_jug + name = "Blender Jug" + desc = "A blender jug, part of a blender." + icon = 'icons/obj/kitchen.dmi' + icon_state = "blender_jug_e" + volume = 100 + + on_reagent_change() + switch(src.reagents.total_volume) + if(0) + icon_state = "blender_jug_e" + if(1 to 75) + icon_state = "blender_jug_h" + if(76 to 100) + icon_state = "blender_jug_f" + +/obj/item/weapon/reagent_containers/glass/canister //not used apparantly + desc = "It's a canister. Mainly used for transporting fuel." + name = "canister" + icon = 'icons/obj/tank.dmi' + icon_state = "canister" + item_state = "canister" + m_amt = 300 + g_amt = 0 + w_class = 4.0 + + amount_per_transfer_from_this = 20 + possible_transfer_amounts = list(10,20,30,60) + volume = 120 + flags = FPRINT + +/obj/item/weapon/reagent_containers/glass/dispenser + name = "reagent glass" + desc = "A reagent glass." + icon = 'icons/obj/chemical.dmi' + icon_state = "beaker0" + amount_per_transfer_from_this = 10 + flags = FPRINT | TABLEPASS | OPENCONTAINER + +/obj/item/weapon/reagent_containers/glass/dispenser/surfactant + name = "reagent glass (surfactant)" + icon_state = "liquid" + + New() + ..() + reagents.add_reagent("fluorosurfactant", 20) + +*/ diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 9ba14f5d74..52f37b9dd2 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -211,6 +211,10 @@ // attack with hand, switch position /obj/machinery/conveyor_switch/attack_hand(mob/user) + if(!allowed(user)) + user << "Access denied." + return + if(position == 0) if(last_pos < 0) position = 1 diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index f3b3709d86..02289aacf8 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -157,7 +157,7 @@ /obj/item/device/destTagger name = "destination tagger" desc = "Used to set the destination of properly wrapped packages." - icon_state = "forensic0-old" + icon_state = "dest_tagger" var/currTag = 0 //The whole system for the sorttype var is determined based on the order of this list, //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index ac14453316..e861826e0f 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -1340,6 +1340,28 @@ datum/design/nanopaste build_type = PROTOLATHE materials = list("$metal" = 7000, "$glass" = 7000) build_path = "/obj/item/stack/nanopaste" + +datum/design/bluespacebeaker + name = "bluespace beaker" + 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("bluespace" = 2, "materials" = 6) + build_type = PROTOLATHE + materials = list("$metal" = 3000, "$plasma" = 3000, "$diamond" = 500) + reliability_base = 76 + build_path = "/obj/item/weapon/reagent_containers/glass/beaker/bluespace" + category = "Misc" + +datum/design/noreactbeaker + name = "cryostasis beaker" + desc = "A cryostasis beaker that allows for chemical storage without reactions. Can hold up to 50 units." + id = "splitbeaker" + req_tech = list("materials" = 2) + build_type = PROTOLATHE + materials = list("$metal" = 3000) + reliability_base = 76 + build_path = "/obj/item/weapon/reagent_containers/glass/beaker/noreact" + category = "Misc" ///////////////////////////////////////// /////////////////Weapons///////////////// ///////////////////////////////////////// diff --git a/code/modules/research/xenoarchaeology/artifact/artifact.dm b/code/modules/research/xenoarchaeology/artifact/artifact.dm index 1ff8a220f3..6300a6d71a 100644 --- a/code/modules/research/xenoarchaeology/artifact/artifact.dm +++ b/code/modules/research/xenoarchaeology/artifact/artifact.dm @@ -35,6 +35,7 @@ icon_state = "boulder1" density = 1 opacity = 1 + anchored = 1 var/excavation_level = 0 var/datum/geosample/geological_data var/datum/artifact_find/artifact_find @@ -75,17 +76,14 @@ user << "\blue You finish [P.drill_verb] [src]." excavation_level += P.excavation_amount - var/reveal_prob = 1 - if(excavation_level >= 95) - reveal_prob = 50 + (excavation_level - 90) * (excavation_level - 90) - else if(excavation_level >= 90) - reveal_prob = 5 - if(excavation_level >= 100) + if(excavation_level > 100) //failure user.visible_message("[src] suddenly crumbles away.",\ - "\red [src] has disintegrated under your onslaught, any secrets it was holding long gone.") + "\red [src] has disintegrated under your onslaught, any secrets it was holding are long gone.") del(src) - else if(prob(reveal_prob)) + return + + if(prob(excavation_level)) //success if(artifact_find) var/spawn_type = artifact_find.artifact_find_type diff --git a/code/modules/research/xenoarchaeology/artifact/artifact_unknown_old.dm b/code/modules/research/xenoarchaeology/artifact/artifact_unknown_old.dm deleted file mode 100644 index 3c779caa96..0000000000 --- a/code/modules/research/xenoarchaeology/artifact/artifact_unknown_old.dm +++ /dev/null @@ -1,242 +0,0 @@ -//Part of ISaidNo's public release around July 2011(ish), multiple changes -//many thanks - -#define PLASMA_SPAWN 1 -#define N2_SPAWN 2 -#define CO2_SPAWN 3 -#define RADIATE 4 -#define VIRUS 5 -#define HEAT 6 -#define COLD 7 - -/obj/machinery/artifact - name = "alien artifact" - desc = "A large alien device." - icon = 'icons/obj/xenoarchaeology.dmi' - icon_state = "ano00" - var/icon_num = 0 - anchored = 0 - density = 1 - var/origin = null // Used in the randomisation/research of the artifact. - var/activated = 0 // Whether or not the artifact has been unlocked. - var/charged = 1 // Whether the artifact is ready to have it's effect. - var/chargetime = 0 // How much time until the artifact is charged. - var/recharge = 5 // How long does it take this artifact to recharge? - var/display_id = "" // Artifact ID to display once successfully scanned - var/datum/artifact_effect/my_effect = null - var/being_used = 0 - -/obj/machinery/artifact/New() - ..() - // Origin and appearance randomisation - - my_effect = new() - - - icon_num = rand(0,5) - icon_state = "ano[icon_num]0" - - // Power randomisation - my_effect.trigger = pick("force","energy","chemical","heat","touch","presence") - if (my_effect.trigger == "chemical") - my_effect.triggerX = pick("hydrogen","corrosive","volatile","toxic") - - my_effect.effecttype = pick("healing","injure","stun","roboheal","robohurt","cellcharge","celldrain","planthelper","forcefield","teleport","dnaswitch","emp","sleepy") - - // Select range based on the power - var/canworldpulse = 1 - switch(my_effect.effecttype) - if("healing") - my_effect.effectmode = pick("aura","pulse","contact") - if("injure") - my_effect.effectmode = pick("aura","pulse","contact") - if("stun") - my_effect.effectmode = pick("aura","pulse","contact") - if("roboheal") - my_effect.effectmode = pick("aura","pulse","contact") - if("robohurt") - my_effect.effectmode = pick("aura","pulse","contact") - if("sleepy") - my_effect.effectmode = pick("aura","pulse","contact") - if("cellcharge") - my_effect.effectmode = pick("aura","pulse") - if("celldrain") - my_effect.effectmode = pick("aura","pulse") - if("planthelper") - my_effect.effectmode = pick("aura","pulse") - canworldpulse = 0 - if("forcefield") - my_effect.effectmode = "contact" - canworldpulse = 0 - if("teleport") - my_effect.effectmode = pick("pulse","contact") - if("genderswitch") - my_effect.effectmode = pick("pulse","contact") - if("emp") - my_effect.effectmode = pick("pulse","contact") - - // Recharge timer & range setup - if (my_effect.effectmode == "aura") - my_effect.aurarange = rand(1,4) - if (my_effect.effectmode == "contact") - src.recharge = rand(5,15) - if (my_effect.effectmode == "pulse") - my_effect.aurarange = rand(2,14) - src.recharge = rand(5,20) - if (canworldpulse == 1 && prob(1)) - my_effect.effectmode = "worldpulse" - src.recharge = rand(40,120) - - /* - display_id += pick("kappa","sigma","antaeres","beta","lorard","omicron","iota","upsilon","omega","gamma","delta") - display_id += "-" - display_id += num2text(rand(100,999)) - */ - -/obj/machinery/artifact/Del() - ..() - my_effect.HaltEffect() - -/obj/machinery/artifact/attack_hand(var/mob/user as mob) - if (istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead/)) return - if (istype(user, /mob/living/silicon/robot)) - if (get_dist(user, src) > 1) - user << "\red You can't reach [src] from here." - return - if(ishuman(user) && istype(user:gloves,/obj/item/clothing/gloves)) - return ..() - for(var/mob/O in viewers(src, null)) - O.show_message(text("[] touches [].", user, src), 1) - src.add_fingerprint(user) - src.Artifact_Contact(user) - -/obj/machinery/artifact/attackby(obj/item/weapon/W as obj, mob/living/user as mob) - /*if (istype(W, /obj/item/weapon/cargotele)) - W:cargoteleport(src, user) - return*/ - if (my_effect.trigger == "chemical" && istype(W, /obj/item/weapon/reagent_containers/)) - switch(my_effect.triggerX) - if("hydrogen") - if (W.reagents.has_reagent("hydrogen", 1) || W.reagents.has_reagent("water", 1)) - src.Artifact_Activate() - return - if("corrosive") - if (W.reagents.has_reagent("acid", 1) || W.reagents.has_reagent("pacid", 1) || W.reagents.has_reagent("diethylamine", 1)) - src.Artifact_Activate() - return - if("volatile") - if (W.reagents.has_reagent("plasma", 1) || W.reagents.has_reagent("thermite", 1)) - src.Artifact_Activate() - return - if("toxic") - if (W.reagents.has_reagent("toxin", 1) || W.reagents.has_reagent("cyanide", 1) || W.reagents.has_reagent("amanitin", 1) || W.reagents.has_reagent("neurotoxin", 1)) - src.Artifact_Activate() - return - ..() - if (my_effect.trigger == "force" && W.force >= 10 && !src.activated) src.Artifact_Activate() - if (my_effect.trigger == "energy") - if (istype(W,/obj/item/weapon/melee/baton) && W:status) src.Artifact_Activate() - if (istype(W,/obj/item/weapon/melee/energy)) src.Artifact_Activate() - if (istype(W,/obj/item/weapon/melee/cultblade)) src.Artifact_Activate() - if (istype(W,/obj/item/weapon/gun/energy/)) src.Artifact_Activate() - if (istype(W,/obj/item/device/multitool)) src.Artifact_Activate() - if (istype(W,/obj/item/weapon/card/emag)) src.Artifact_Activate() - if (my_effect.trigger == "heat") - if (istype(W,/obj/item/weapon/match) && W:lit) src.Artifact_Activate() - if (istype(W, /obj/item/weapon/weldingtool) && W:welding) src.Artifact_Activate() - if (istype(W, /obj/item/weapon/lighter) && W:lit) src.Artifact_Activate() - - //Bump(atom/A) - -/obj/machinery/artifact/Bumped(M as mob|obj) - if (istype(M,/obj/item/weapon/) && my_effect.trigger == "force" && M:throwforce >= 10) src.Artifact_Activate() - -/obj/machinery/artifact/bullet_act(var/obj/item/projectile/P) - if (my_effect.trigger == "force") - if(istype(P,/obj/item/projectile/bullet)) src.Artifact_Activate() - else if(istype(P,/obj/item/projectile/hivebotbullet)) src.Artifact_Activate() - if (my_effect.trigger == "energy") - if(istype(P,/obj/item/projectile/beam)) src.Artifact_Activate() - else if(istype(P,/obj/item/projectile/ion)) src.Artifact_Activate() - else if(istype(P,/obj/item/projectile/energy)) src.Artifact_Activate() - if (my_effect.trigger == "heat") - if(istype(P,/obj/item/projectile/temp)) src.Artifact_Activate() - -/obj/machinery/artifact/ex_act(severity) - switch(severity) - if(1.0) del src - if(2.0) - if (prob(50)) del src - if (my_effect.trigger == "force") src.Artifact_Activate() - if (my_effect.trigger == "heat") src.Artifact_Activate() - if(3.0) - if (my_effect.trigger == "force") src.Artifact_Activate() - if (my_effect.trigger == "heat") src.Artifact_Activate() - return - -/obj/machinery/artifact/temperature_expose(null, temp, volume) - if (my_effect.trigger == "heat") src.Artifact_Activate() - -/obj/machinery/artifact/process() - if (!src.activated) - return - if (chargetime > 0) - chargetime -= 1 - else - src.charged = 1 - - my_effect.UpdateEffect(src.loc) - - //activate - if( (my_effect.effectmode == "pulse" || my_effect.effecttype == "worldpulse") && activated) - if(src.charged && my_effect.DoEffect(src)) - src.charged = 0 - src.chargetime = src.recharge - -/obj/machinery/artifact/proc/Artifact_Activate() - src.activated = !src.activated - var/display_msg = "" - if(activated) - if(prob(30)) - switch(rand(4)) - if(0) - display_msg = "momentarily glows brightly!" - if(1) - display_msg = "distorts slightly for a moment!" - if(2) - display_msg = "makes a slightly clicking noise!" - if(3) - display_msg = "flickers slightly!" - if(4) - display_msg = "vibrates!" - else - my_effect.HaltEffect() - if(prob(30)) - switch(rand(2)) - if(0) - display_msg = "grows dull!" - if(1) - display_msg = "fades in intensity!" - if(2) - display_msg = "suddenly becomes very quiet!" - - icon_state = "ano[icon_num][activated]" - for(var/mob/O in viewers(src, null)) - O.show_message(text("[] [display_msg]", src), 1) - -/obj/machinery/artifact/proc/Artifact_Contact(var/mob/user as mob) - // Trigger Code - if (istype (user,/mob/living/carbon/) && my_effect.trigger == "touch" && !src.activated) src.Artifact_Activate() - else if (my_effect.trigger != "touch" && !src.activated) user << "Nothing happens." - - if (my_effect.effectmode == "contact" && src.activated && src.charged) - my_effect.DoEffect(user) - src.charged = 0 - src.chargetime = src.recharge - -// this was used in QM for a time but it fell into disuse and wasn't removed, the purpose being to check if an artifact -// was benevolent or malicious, to determine whether QMs would be paid or punished for shipping it - -/obj/machinery/artifact/Move() - ..() - my_effect.update_move(src, src.loc) diff --git a/code/modules/research/xenoarchaeology/artifact/effect_old.dm b/code/modules/research/xenoarchaeology/artifact/effect_old.dm deleted file mode 100644 index 092cb0d0f5..0000000000 --- a/code/modules/research/xenoarchaeology/artifact/effect_old.dm +++ /dev/null @@ -1,542 +0,0 @@ -// - -/datum/artifact_effect - var/artifact_id = "" // Display ID of the spawning artifact - var/trigger = "touch" // What activates it? - var/triggerX = "none" // Used for more varied triggers - var/effecttype = "healing" // What does it do? - var/effectmode = "aura" // How does it carry out the effect? - var/aurarange = 4 // How far the artifact will extend an aura effect. - var/list/created_field - var/archived_loc - -/datum/artifact_effect/New() - // - created_field = new() - -/datum/artifact_effect/proc/GetOriginString(var/origin) - -/datum/artifact_effect/proc/GetEffectString(var/effect) - -/datum/artifact_effect/proc/GetTriggerString(var/trigger) - -/datum/artifact_effect/proc/GetRangeString(var/range) - switch(effectmode) - if("aura") return "Constant Short-Range Energy Field" - if("pulse") - if(aurarange > 7) return "Long Range Energy Pulses" - else return "Medium Range Energy Pulses" - if("worldpulse") return "Extreme Range Energy Pulses" - if("contact") return "Requires contact with subject" - else return "Unknown Range" - -/datum/artifact_effect/proc/HaltEffect() - for(var/obj/effect/energy_field/F in created_field) - created_field.Remove(F) - del F - -/datum/artifact_effect/proc/UpdateEffect(var/atom/originator) - /*for(var/obj/effect/energy_field/F in created_field) - created_field.Remove(F) - del F*/ - if(originator.loc != archived_loc) - archived_loc = originator.loc - update_move(originator) - - for(var/obj/effect/energy_field/E in created_field) - if(E.strength < 5) - E.Strengthen(0.2) - -/datum/artifact_effect/proc/DoEffect(var/atom/originator) - archived_loc = originator.loc - if (src.effectmode == "contact") - var/mob/living/user = originator - if(!user) - return - switch(src.effecttype) - if("healing") - //caeltodo - if (istype(user, /mob/living/carbon/human/)) - user << "\blue You feel a soothing energy invigorate you." - - var/mob/living/carbon/human/H = user - for(var/datum/organ/external/affecting in H.organs) - if(!affecting) continue - if(!istype(affecting, /datum/organ/external)) continue - affecting.heal_damage(25, 25) //fixes getting hit after ingestion, killing you when game updates organ health - //user:heal_organ_damage(25, 25) - // - user.adjustOxyLoss(-25) - user.adjustToxLoss(-25) - user.adjustBruteLoss(-25) - user.adjustFireLoss(-25) - user.adjustBrainLoss(-25) - user.radiation -= min(user.radiation, 25) - user.nutrition += 50 - H.bodytemperature = initial(H.bodytemperature) - // - H.vessel.add_reagent("blood",50) - spawn(1) - H.fixblood() - H.regenerate_icons() - return 1 - // - if (istype(user, /mob/living/carbon/monkey/)) - user << "\blue You feel a soothing energy invigorate you." - user.adjustOxyLoss(-25) - user.adjustToxLoss(-25) - user.adjustBruteLoss(-25) - user.adjustFireLoss(-25) - user.adjustBrainLoss(-25) - return 1 - else user << "Nothing happens." - if("injure") - if (istype(user, /mob/living/carbon/)) - user << "\red A painful discharge of energy strikes you!" - user.adjustOxyLoss(rand(5,25)) - user.adjustToxLoss(rand(5,25)) - user.adjustBruteLoss(rand(5,25)) - user.adjustFireLoss(rand(5,25)) - user.adjustBrainLoss(rand(5,25)) - user.radiation += 25 - user.nutrition -= min(50, user.nutrition) - user.make_dizzy(6) - user.weakened += 6 - return 1 - else user << "Nothing happens." - if("stun") - if (istype(user, /mob/living/carbon/)) - user << "\red A powerful force overwhelms your consciousness." - user.weakened += 45 - user.stuttering += 45 - if(prob(50)) - user.stunned += rand(1,10) - return 1 - else user << "Nothing happens." - if("roboheal") - if (istype(user, /mob/living/silicon/robot)) - user << "\blue Your systems report damaged components mending by themselves!" - user.adjustBruteLoss(rand(-10,-30)) - user.adjustFireLoss(rand(-10,-30)) - return 1 - else user << "Nothing happens." - if("robohurt") - if (istype(user, /mob/living/silicon/robot)) - user << "\red Your systems report severe damage has been inflicted!" - user.adjustBruteLoss(rand(10,50)) - user.adjustFireLoss(rand(10,50)) - return 1 - else user << "Nothing happens." - if("forcefield") - while(created_field.len < 16) - var/obj/effect/energy_field/E = new (locate(user.x,user.y,user.z)) - created_field.Add(E) - E.strength = 1 - E.density = 1 - E.anchored = 1 - E.invisibility = 0 - return 1 - if("teleport") - var/list/randomturfs = new/list() - for(var/turf/T in orange(user, 50)) - if(!istype(T, /turf/simulated/floor) || T.density) - continue - randomturfs.Add(T) - if(randomturfs.len > 0) - user << "\red You are suddenly zapped away elsewhere!" - if (user.buckled) - user.buckled.unbuckle() - user.loc = pick(randomturfs) - var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() - sparks.set_up(3, 0, get_turf(originator)) //no idea what the 0 is - sparks.start() - return 1 - if("sleepy") - user << pick("\blue You feel like taking a nap.","\blue You feel a yawn coming on.","\blue You feel a little tired.") - user.drowsyness = min(user.drowsyness + rand(5,25), 50) - user.eye_blurry = min(user.eye_blurry + rand(1,3), 50) - return 1 - else if (src.effectmode == "aura") - switch(src.effecttype) - //caeltodo - if("healing") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - if(prob(10)) M << "\blue You feel a soothing energy radiating from something nearby." - M.adjustBruteLoss(-1) - M.adjustFireLoss(-1) - M.adjustToxLoss(-1) - M.adjustOxyLoss(-1) - M.adjustBrainLoss(-1) - M.updatehealth() - return 1 - if("injure") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - if(prob(10)) M << "\red You feel a painful force radiating from something nearby." - M.adjustBruteLoss(1) - M.adjustFireLoss(1) - M.adjustToxLoss(1) - M.adjustOxyLoss(1) - M.adjustBrainLoss(1) - M.updatehealth() - return 1 - if("stun") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - if(prob(10)) M << "\red Energy radiating from the [originator] is making you feel numb." - if(prob(20)) - M << "\red Your body goes numb for a moment." - M.stunned += 2 - M.weakened += 2 - M.stuttering += 2 - return 1 - if("roboheal") - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - if(prob(10)) M << "\blue SYSTEM ALERT: Beneficial energy field detected!" - M.adjustBruteLoss(-1) - M.adjustFireLoss(-1) - M.updatehealth() - return 1 - if("robohurt") - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - if(prob(10)) M << "\red SYSTEM ALERT: Harmful energy field detected!" - M.adjustBruteLoss(1) - M.adjustFireLoss(1) - M.updatehealth() - return 1 - if("cellcharge") - for (var/obj/machinery/power/apc/C in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/B in C.contents) - B.charge += 10 - for (var/obj/machinery/power/smes/S in range (src.aurarange,originator)) S.charge += 20 - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/D in M.contents) - D.charge += 10 - if(prob(10)) M << "\blue SYSTEM ALERT: Energy boosting field detected!" - return 1 - if("celldrain") - for (var/obj/machinery/power/apc/C in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/B in C.contents) - B.charge = max(B.charge-10,0) - for (var/obj/machinery/power/smes/S in range (src.aurarange,originator)) - S.charge = max(S.charge-20,0) - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/D in M.contents) - D.charge = max(D.charge-10,0) - if(prob(10)) M << "\red SYSTEM ALERT: Energy draining field detected!" - return 1 - if("planthelper") - for (var/obj/machinery/hydroponics/H in range(src.aurarange,originator)) - //makes weeds and shrooms and stuff more potent too - if(H.planted) - H.waterlevel += 2 - H.nutrilevel += 2 - if(H.toxic > 0) - H.toxic -= 1 - H.health += 1 - if(H.pestlevel > 0) - H.pestlevel -= 1 - if(H.weedlevel > 0) - H.weedlevel -= 1 - H.lastcycle += 5 - return 1 - if("sleepy") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(prob(10)) - M << pick("\blue You feel like taking a nap.","\blue You feel a yawn coming on.","\blue You feel a little tired.") - M.drowsyness = min(M.drowsyness + 1, 25) - M.eye_blurry = min(M.eye_blurry + 1, 25) - return 1 - else if (src.effectmode == "pulse") - for(var/mob/O in viewers(originator, null)) - O.show_message(text("[] emits a pulse of energy!", originator), 1) - switch(src.effecttype) - //caeltodo - if("healing") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - M << "\blue A wave of energy invigorates you." - M.adjustBruteLoss(-5) - M.adjustFireLoss(-5) - M.adjustToxLoss(-5) - M.adjustOxyLoss(-5) - M.adjustBrainLoss(-5) - M.updatehealth() - return 1 - if("injure") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - M << "\red A wave of energy causes you great pain!" - M.adjustBruteLoss(5) - M.adjustFireLoss(5) - M.adjustToxLoss(5) - M.adjustOxyLoss(5) - M.adjustBrainLoss(5) - M.make_dizzy(6) - M.weakened += 3 - M.updatehealth() - return 1 - if("stun") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - M << "\red A wave of energy overwhelms your senses!" - M.paralysis += 3 - M.weakened += 4 - M.stuttering += 4 - return 1 - if("roboheal") - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - M << "\blue SYSTEM ALERT: Structural damage has been repaired by energy pulse!" - M.adjustBruteLoss(-10) - M.adjustFireLoss(-10) - M.updatehealth() - return 1 - if("robohurt") - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - M << "\red SYSTEM ALERT: Structural damage inflicted by energy pulse!" - M.adjustBruteLoss(10) - M.adjustFireLoss(10) - M.updatehealth() - return 1 - if("cellcharge") - for (var/obj/machinery/power/apc/C in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/B in C.contents) - B.charge += 250 - for (var/obj/machinery/power/smes/S in range (src.aurarange,originator)) S.charge += 400 - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/D in M.contents) - D.charge += 250 - M << "\blue SYSTEM ALERT: Large energy boost detected!" - return 1 - if("celldrain") - for (var/obj/machinery/power/apc/C in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/B in C.contents) - B.charge = max(B.charge-500,0) - for (var/obj/machinery/power/smes/S in range (src.aurarange,originator)) - S.charge = max(S.charge-400,0) - for (var/mob/living/silicon/robot/M in range(src.aurarange,originator)) - for (var/obj/item/weapon/cell/D in M.contents) - D.charge = max(D.charge-500,0) - M << "\red SYSTEM ALERT: Severe energy drain detected!" - return 1 - if("planthelper") - //makes weeds and shrooms and stuff more potent too - for (var/obj/machinery/hydroponics/H in range(src.aurarange,originator)) - if(H.planted) - H.dead = 0 - H.waterlevel = 200 - H.nutrilevel = 200 - H.toxic = 0 - H.health = 100 - H.pestlevel = 0 - H.weedlevel = 0 - H.lastcycle = H.cycledelay - return 1 - if("teleport") - for (var/mob/living/M in range(src.aurarange,originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - var/list/randomturfs = new/list() - for(var/turf/T in orange(M, 30)) - if(!istype(T, /turf/simulated/floor) || T.density) - continue - randomturfs.Add(T) - if(randomturfs.len > 0) - M << "\red You are displaced by a strange force!" - if(M.buckled) - M.buckled.unbuckle() - M.loc = pick(randomturfs) - var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() - sparks.set_up(3, 0, get_turf(originator)) //no idea what the 0 is - sparks.start() - return 1 - if("dnaswitch") - for(var/mob/living/H in range(src.aurarange,originator)) - if(ishuman(H) && istype(H:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(H:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - - if(prob(30)) - H << pick("\green You feel a little different.","\green You feel strange.","\green You feel different.") - //todo - if (H.gender == FEMALE) - H.gender = MALE - else - H.gender = FEMALE - /*H.dna.ready_dna(H) - H.update_body() - H.update_face()*/ - return 1 - if("emp") - empulse(get_turf(originator), aurarange/2, aurarange) - return 1 - if("sleepy") - for (var/mob/living/carbon/M in range(src.aurarange,originator)) - if(prob(30)) - M << pick("\blue You feel like taking a nap.","\blue You feel a yawn coming on.","\blue You feel a little tired.") - if(prob(50)) - M.drowsyness = min(M.drowsyness + rand(1,5), 25) - if(prob(50)) - M.eye_blurry = min(M.eye_blurry + rand(1,5), 25) - return 1 - else if (src.effectmode == "worldpulse") - for(var/mob/O in viewers(originator, null)) - O.show_message(text("[] emits a powerful burst of energy!", originator), 1) - switch(src.effecttype) - if("healing") - for (var/mob/living/carbon/M in range(200, originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - M << "\blue Waves of soothing energy wash over you." - M.adjustBruteLoss(-3) - M.adjustFireLoss(-3) - M.adjustToxLoss(-3) - M.adjustOxyLoss(-3) - M.adjustBrainLoss(-3) - M.updatehealth() - return 1 - if("injure") - for (var/mob/living/carbon/human/M in range(200, originator)) - M << "\red A wave of painful energy strikes you!" - M.adjustBruteLoss(3) - M.adjustFireLoss(3) - M.adjustToxLoss(3) - M.adjustOxyLoss(3) - M.adjustBrainLoss(3) - M.updatehealth() - return 1 - if("stun") - for (var/mob/living/carbon/M in range(200, originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - M << "\red A powerful force causes you to black out momentarily." - M.paralysis += 5 - M.weakened += 8 - M.stuttering += 8 - return 1 - if("roboheal") - for (var/mob/living/silicon/robot/M in range(200, originator)) - M << "\blue SYSTEM ALERT: Structural damage has been repaired by energy pulse!" - M.adjustBruteLoss(-5) - M.adjustFireLoss(-5) - M.updatehealth() - return 1 - if("robohurt") - for (var/mob/living/silicon/robot/M in range(200, originator)) - M << "\red SYSTEM ALERT: Structural damage inflicted by energy pulse!" - M.adjustBruteLoss(5) - M.adjustFireLoss(5) - M.updatehealth() - return 1 - if("cellcharge") - for (var/obj/machinery/power/apc/C in range(200, originator)) - for (var/obj/item/weapon/cell/B in C.contents) - B.charge += 100 - for (var/obj/machinery/power/smes/S in range (src.aurarange,src)) S.charge += 250 - for (var/mob/living/silicon/robot/M in world) - for (var/obj/item/weapon/cell/D in M.contents) - D.charge += 100 - M << "\blue SYSTEM ALERT: Energy boost detected!" - return 1 - if("celldrain") - for (var/obj/machinery/power/apc/C in range(200, originator)) - for (var/obj/item/weapon/cell/B in C.contents) - B.charge = max(B.charge-250,0) - for (var/obj/machinery/power/smes/S in range (src.aurarange,src)) - S.charge = max(S.charge-250,0) - for (var/mob/living/silicon/robot/M in world) - for (var/obj/item/weapon/cell/D in M.contents) - D.charge = max(D.charge-250,0) - M << "\red SYSTEM ALERT: Energy drain detected!" - return 1 - if("teleport") - for (var/mob/living/M in range(200, originator)) - if(ishuman(M) && istype(M:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(M:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - var/list/randomturfs = new/list() - for(var/turf/T in orange(M, 15)) - if(!istype(T, /turf/simulated/floor) || T.density) - continue - randomturfs.Add(T) - if(randomturfs.len > 0) - M << "\red You are displaced by a strange force!" - if(M.buckled) - M.buckled.unbuckle() - M.loc = pick(randomturfs) - var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() - sparks.set_up(3, 0, get_turf(originator)) //no idea what the 0 is - sparks.start() - return 1 - if("dnaswitch") - for(var/mob/living/H in range(200, originator)) - if(ishuman(H) && istype(H:wear_suit,/obj/item/clothing/suit/bio_suit/anomaly) && istype(H:head,/obj/item/clothing/head/bio_hood/anomaly)) - continue - - if(prob(30)) - H << pick("\green You feel a little different.","\green You feel strange.","\green You feel different.") - //todo - if (H.gender == FEMALE) - H.gender = MALE - else - H.gender = FEMALE - /*H.dna.ready_dna(H) - H.update_body() - H.update_face()*/ - return 1 - if("sleepy") - for(var/mob/living/H in range(200, originator)) - H.drowsyness = min(H.drowsyness + rand(5,15), 50) - H.eye_blurry = min(H.eye_blurry + rand(5,15), 50) - return 1 - -//initially for the force field artifact -/datum/artifact_effect/proc/update_move(var/atom/originator) - switch(effecttype) - if("forcefield") - while(created_field.len < 16) - //for now, just instantly respawn the fields when they get destroyed - var/obj/effect/energy_field/E = new (locate(originator.x,originator.y,originator)) - created_field.Add(E) - E.strength = 1 - E.density = 1 - E.anchored = 1 - E.invisibility = 0 - - var/obj/effect/energy_field/E = created_field[1] - E.loc = locate(originator.x + 2,originator.y + 2,originator.z) - E = created_field[2] - E.loc = locate(originator.x + 2,originator.y + 1,originator.z) - E = created_field[3] - E.loc = locate(originator.x + 2,originator.y,originator.z) - E = created_field[4] - E.loc = locate(originator.x + 2,originator.y - 1,originator.z) - E = created_field[5] - E.loc = locate(originator.x + 2,originator.y - 2,originator.z) - E = created_field[6] - E.loc = locate(originator.x + 1,originator.y + 2,originator.z) - E = created_field[7] - E.loc = locate(originator.x + 1,originator.y - 2,originator.z) - E = created_field[8] - E.loc = locate(originator.x,originator.y + 2,originator.z) - E = created_field[9] - E.loc = locate(originator.x,originator.y - 2,originator.z) - E = created_field[10] - E.loc = locate(originator.x - 1,originator.y + 2,originator.z) - E = created_field[11] - E.loc = locate(originator.x - 1,originator.y - 2,originator.z) - E = created_field[12] - E.loc = locate(originator.x - 2,originator.y + 2,originator.z) - E = created_field[13] - E.loc = locate(originator.x - 2,originator.y + 1,originator.z) - E = created_field[14] - E.loc = locate(originator.x - 2,originator.y,originator.z) - E = created_field[15] - E.loc = locate(originator.x - 2,originator.y - 1,originator.z) - E = created_field[16] - E.loc = locate(originator.x - 2,originator.y - 2,originator.z) diff --git a/code/modules/research/xenoarchaeology/finds/finds.dm b/code/modules/research/xenoarchaeology/finds/finds.dm index 7464e7c8b0..0b776ca91f 100644 --- a/code/modules/research/xenoarchaeology/finds/finds.dm +++ b/code/modules/research/xenoarchaeology/finds/finds.dm @@ -324,26 +324,27 @@ if(26) //energy gun var/spawn_type = pick(\ - /obj/item/weapon/gun/energy/laser/practice;100,\ - /obj/item/weapon/gun/energy/laser;75,\ - /obj/item/weapon/gun/energy/xray;50,\ - /obj/item/weapon/gun/energy/laser/captain;25,\ - ) - var/obj/item/weapon/gun/energy/new_gun = new spawn_type(src.loc) - new_item = new_gun - new_item.icon_state = "egun[rand(1,6)]" + /obj/item/weapon/gun/energy/laser/practice,\ + /obj/item/weapon/gun/energy/laser,\ + /obj/item/weapon/gun/energy/xray,\ + /obj/item/weapon/gun/energy/laser/captain) + if(spawn_type) + var/obj/item/weapon/gun/energy/new_gun = new spawn_type(src.loc) + new_item = new_gun + new_item.icon_state = "egun[rand(1,6)]" + new_gun.desc = "This is an antique energy weapon, you're not sure if it will fire or not." - //5% chance to explode when first fired - //10% chance to have an unchargeable cell - //15% chance to gain a random amount of starting energy, otherwise start with an empty cell - if(prob(5)) - new_gun.power_supply.rigged = 1 - if(prob(10)) - new_gun.power_supply.maxcharge = 0 - if(prob(15)) - new_gun.power_supply.charge = rand(0, new_gun.power_supply.maxcharge) - else - new_gun.power_supply.charge = 0 + //5% chance to explode when first fired + //10% chance to have an unchargeable cell + //15% chance to gain a random amount of starting energy, otherwise start with an empty cell + if(prob(5)) + new_gun.power_supply.rigged = 1 + if(prob(10)) + new_gun.power_supply.maxcharge = 0 + if(prob(15)) + new_gun.power_supply.charge = rand(0, new_gun.power_supply.maxcharge) + else + new_gun.power_supply.charge = 0 item_type = "gun" if(27) diff --git a/code/modules/research/xenoarchaeology/finds/finds_defines.dm b/code/modules/research/xenoarchaeology/finds/finds_defines.dm index b9649d074b..6de9b4c806 100644 --- a/code/modules/research/xenoarchaeology/finds/finds_defines.dm +++ b/code/modules/research/xenoarchaeology/finds/finds_defines.dm @@ -206,6 +206,28 @@ ) return find_type +var/list/responsive_carriers = list( \ + "carbon", \ + "potassium", \ + "hydrogen", \ + "nitrogen", \ + "mercury", \ + "iron", \ + "chlorine", \ + "phosphorus", \ + "plasma") + +var/list/finds_as_strings = list( \ + "Trace organic cells", \ + "Long exposure particles", \ + "Trace water particles", \ + "Crystalline structures", \ + "Metallic derivative", \ + "Metallic composite", \ + "Metamorphic/igneous rock composite", \ + "Metamorphic/sedimentary rock composite", \ + "Anomalous material" ) + #undef ARCHAEO_BOWL #undef ARCHAEO_URN #undef ARCHAEO_CUTLERY diff --git a/code/modules/research/xenoarchaeology/finds/finds_misc.dm b/code/modules/research/xenoarchaeology/finds/finds_misc.dm index 64184ec0b6..0c626b8581 100644 --- a/code/modules/research/xenoarchaeology/finds/finds_misc.dm +++ b/code/modules/research/xenoarchaeology/finds/finds_misc.dm @@ -1,8 +1,10 @@ /obj/item/weapon/shard/plasma name = "plasma shard" + desc = "A shard of plasma glass. Considerably tougher then normal glass shards. Apparently not tough enough to be a window." + force = 8.0 + throwforce = 15.0 icon_state = "plasmalarge" - /obj/item/weapon/shard/plasma/New() src.icon_state = pick("plasmalarge", "plasmamedium", "plasmasmall") @@ -24,14 +26,14 @@ if ( istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W if(WT.remove_fuel(0, user)) - var/obj/item/stack/sheet/mineral/plasma/NG = new (user.loc) - for (var/obj/item/stack/sheet/mineral/plasma/G in user.loc) + var/obj/item/stack/sheet/glass/plasmaglass/NG = new (user.loc) + for (var/obj/item/stack/sheet/glass/plasmaglass/G in user.loc) if(G==NG) continue if(G.amount>=G.max_amount) continue G.attackby(NG, user) - usr << "You add the newly-formed plasma to the stack. It now contains [NG.amount] sheets." + usr << "You add the newly-formed plasma glass to the stack. It now contains [NG.amount] sheets." //SN src = null del(src) return diff --git a/code/modules/research/xenoarchaeology/geosample.dm b/code/modules/research/xenoarchaeology/geosample.dm index 91d5f1bd62..47a5bd3da1 100644 --- a/code/modules/research/xenoarchaeology/geosample.dm +++ b/code/modules/research/xenoarchaeology/geosample.dm @@ -29,30 +29,6 @@ pixel_x = rand(0,16)-8 pixel_y = rand(0,8)-8 -var/list/responsive_carriers = list( \ - "carbon", \ - "potassium", \ - "hydrogen", \ - "nitrogen", \ - "mercury", \ - "iron", \ - "chlorine", \ - "phosphorus", \ - "plasma") - -var/list/finds_as_strings = list( \ - "Trace organic cells", \ - "Long exposure particles", \ - "Trace water particles", \ - "Crystalline structures", \ - "Metallic derivative", \ - "Metallic composite", \ - "Metamorphic/igneous rock composite", \ - "Metamorphic/sedimentary rock composite", \ - "Anomalous material" ) - -var/list/artifact_spawning_turfs = list() - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Geosample datum @@ -64,7 +40,6 @@ var/list/artifact_spawning_turfs = list() var/artifact_id = "" //id of a nearby artifact, if there is one var/artifact_distance = -1 //proportional to distance var/source_mineral = "chlorine" //machines will pop up a warning telling players that the sample may be confused - var/total_spread = 0 // //var/source_mineral //all potential finds are initialised to null, so nullcheck before you access them @@ -76,11 +51,11 @@ var/list/artifact_spawning_turfs = list() //this should only need to be called once /datum/geosample/proc/UpdateTurf(var/turf/simulated/mineral/container) + set background = 1 if(!container || !istype(container)) return age = rand(1,999) - total_spread = 0 switch(container.mineralName) if("Uranium") @@ -132,8 +107,15 @@ var/list/artifact_spawning_turfs = list() var/responsive_reagent = get_responsive_reagent(F.find_type) find_presence[responsive_reagent] = F.dissonance_spread - for(var/entry in find_presence) - total_spread += find_presence[entry] + //loop over again to reset values to percentages + var/total_presence = 0 + for(var/carrier in find_presence) + total_presence += find_presence[carrier] + for(var/carrier in find_presence) + find_presence[carrier] = find_presence[carrier] / total_presence + + /*for(var/entry in find_presence) + total_spread += find_presence[entry]*/ //have this separate from UpdateTurf() so that we dont have a billion turfs being updated (redundantly) every time an artifact spawns /datum/geosample/proc/UpdateNearbyArtifactInfo(var/turf/simulated/mineral/container) @@ -144,14 +126,14 @@ var/list/artifact_spawning_turfs = list() artifact_distance = rand() artifact_id = container.artifact_find.artifact_id else - for(var/turf/simulated/mineral/holder in artifact_spawning_turfs) - if(holder.artifact_find) - var/dist = get_dist(container, holder) - if(dist < holder.artifact_find.artifact_detect_range && dist < src.artifact_distance) - src.artifact_distance = dist - src.artifact_id = holder.artifact_find.artifact_id + for(var/turf/simulated/mineral/T in master_controller.artifact_spawning_turfs) + if(T.artifact_find) + var/cur_dist = get_dist(container, T) * 2 + if( (artifact_distance < 0 || cur_dist < artifact_distance) && cur_dist <= T.artifact_find.artifact_detect_range ) + artifact_distance = cur_dist + rand() * 2 - 1 + artifact_id = T.artifact_find.artifact_id else - artifact_spawning_turfs.Remove(holder) + master_controller.artifact_spawning_turfs.Remove(T) /* #undef FIND_PLANT diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_accelerator.dm b/code/modules/research/xenoarchaeology/machinery/analysis_accelerator.dm deleted file mode 100644 index 74ac904dc9..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_accelerator.dm +++ /dev/null @@ -1,44 +0,0 @@ - -// This machine shows the age for extremely old finds - -obj/machinery/anomaly/accelerator - name = "Accelerator spectrometer" - -obj/machinery/anomaly/accelerator/ScanResults() - var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." - - var/datum/geosample/scanned_sample - var/carrier_name - var/num_reagents = 0 - - for(var/datum/reagent/A in held_container.reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/analysis_sample)) - scanned_sample = R.data - else - carrier_name = R.id - num_reagents++ - - if(num_reagents == 2 && scanned_sample && carrier_name) - var/specifity = GetResultSpecifity(scanned_sample, carrier_name) - results = "Kinetic acceleration of carrier ([carrier_name]) indicates age ([100 * specifity]% accuracy):

    " - - if(scanned_sample.age_billion) - var/displayed_age_millions = scanned_sample.age_million + max(scanned_sample.age_million * ((1 - specifity) * (2 * rand() - 1)), 0) - var/displayed_age_billions = scanned_sample.age_billion + max(scanned_sample.age_billion * ((1 - specifity) * (2 * rand() - 1)), 0) - results += "[displayed_age_billions + displayed_age_millions / 1000] billion years.
    " - else if(scanned_sample.age_million) - var/displayed_age_thousands = scanned_sample.age_thousand + max(scanned_sample.age_thousand * ((1 - specifity) * (4 * rand() - 2)), 0) - var/displayed_age_millions = scanned_sample.age_million + max(scanned_sample.age_million * ((1 - specifity) * (2 * rand() - 1)), 0) - results += "[displayed_age_millions + displayed_age_thousands / 1000] million years.
    " - else if(scanned_sample.age_thousand) - var/displayed_age = scanned_sample.age + max(scanned_sample.age * ((1 - specifity) * (8 * rand() - 4)), 0) - var/displayed_age_thousands = scanned_sample.age_thousand + max(scanned_sample.age * ((1 - specifity) * (4 * rand() - 2)), 0) - results += "[displayed_age_thousands + displayed_age / 1000] thousand years.
    " - else - var/displayed_age = scanned_sample.age + max(scanned_sample.age * ((1 - specifity) * (8 * rand() - 4)), 0) - results += "[displayed_age] years.
    " - - results += "
    Warning, results only valid for ages on the scale of billions of years." - - return results diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_base.dm b/code/modules/research/xenoarchaeology/machinery/analysis_base.dm deleted file mode 100644 index a462bd98fa..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_base.dm +++ /dev/null @@ -1,203 +0,0 @@ -//Handles how much the temperature changes on power use. (Joules/Kelvin) -//Equates to as much heat energy per kelvin as a quarter tile of air. -#define XENOARCH_HEAT_CAPACITY 5000 - -//Handles heat transfer to the air. (In watts) -//Can heat a single tile 2 degrees per tick. -#define XENOARCH_MAX_ENERGY_TRANSFER 4000 - -//How many joules of electrical energy produce how many joules of heat energy? -#define XENOARCH_HEAT_COEFFICIENT 3 - - -/obj/machinery/anomaly - name = "Analysis machine" - desc = "A specialised, complex analysis machine." - anchored = 1 - density = 1 - icon = 'icons/obj/virology.dmi' - icon_state = "analyser" - - idle_power_usage = 20 //watts - active_power_usage = 300 //Because I need to make up numbers~ - - var/obj/item/weapon/reagent_containers/glass/held_container - var/obj/item/weapon/tank/fuel_container - var/target_scan_ticks = 30 - var/report_num = 0 - var/scan_process = 0 - var/temperature = 273 //measured in kelvin, if this exceeds 1200, the machine is damaged and requires repairs - //if this exceeds 600 and safety is enabled it will shutdown - //temp greater than 600 also requires a safety prompt to initiate scanning - var/max_temp = 450 - -/obj/machinery/anomaly/New() - ..() - - //for analysis debugging - /*var/obj/item/weapon/reagent_containers/glass/solution_tray/S = new(src.loc) - var/turf/simulated/mineral/diamond/D - for(var/turf/simulated/mineral/diamond/M in world) - D = M - break - S.reagents.add_reagent("analysis_sample", 1, D.geological_data) - S.reagents.add_reagent("chlorine", 1, null)*/ - -/obj/machinery/anomaly/process() - //not sure if everything needs to heat up, or just the GLPC - var/datum/gas_mixture/env = loc.return_air() - var/environmental_temp = env.temperature - if(scan_process) - if(scan_process++ > target_scan_ticks) - FinishScan() - else if(temperature > 400) - src.visible_message("\blue \icon[src] shuts down from the heat!", 2) - scan_process = 0 - else if(temperature > 350 && prob(10)) - src.visible_message("\blue \icon[src] bleets plaintively.", 2) - if(temperature > 400) - scan_process = 0 - - //show we're busy - if(prob(5)) - src.visible_message("\blue \icon[src] [pick("whirrs","chuffs","clicks")][pick(" quietly"," softly"," sadly"," excitedly"," energetically"," angrily"," plaintively")].", 2) - - use_power = 2 - - else - use_power = 1 - - auto_use_power() - - //Add 3000 joules when active. This is about 0.6 degrees per tick. - //May need adjustment - if(use_power == 1) - var/heat_added = active_power_usage *XENOARCH_HEAT_COEFFICIENT - - if(temperature < max_temp) - temperature += heat_added/XENOARCH_HEAT_CAPACITY - - var/temperature_difference = abs(environmental_temp-temperature) - var/datum/gas_mixture/removed = loc.remove_air(env.total_moles*0.25) - var/heat_capacity = removed.heat_capacity() - - heat_added = max(temperature_difference*heat_capacity, XENOARCH_MAX_ENERGY_TRANSFER) - - if(temperature > environmental_temp) - //cool down to match the air - temperature = max(TCMB, temperature - heat_added/XENOARCH_HEAT_CAPACITY) - removed.temperature = max(TCMB, removed.temperature + heat_added/heat_capacity) - - if(temperature_difference > 10 && prob(5)) - src.visible_message("\blue \icon[src] hisses softly.", 2) - - else - //heat up to match the air - temperature = max(TCMB, temperature + heat_added/XENOARCH_HEAT_CAPACITY) - removed.temperature = max(TCMB, removed.temperature - heat_added/heat_capacity) - - if(temperature_difference > 10 && prob(5)) - src.visible_message("\blue \icon[src] plinks quietly.", 2) - - env.merge(removed) - - -//this proc should be overriden by each individual machine -/obj/machinery/anomaly/attack_hand(var/mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - user.machine = src - var/dat = "[src.name]
    " - dat += "Module heat level: [temperature] kelvin
    " - dat += "Safeties set at 350k, shielding failure at 400k. Failure to maintain safe heat levels may result in equipment damage.
    " - dat += "
    " - if(scan_process) - dat += "Scan in progress


    " - else - dat += "[held_container ? "Eject beaker" : "No beaker inserted."]
    " - //dat += "[fuel_container ? "Eject fuel tank" : "No fuel tank inserted."]
    " - dat += "[held_container ? "Begin scanning" : ""]" - dat += "
    " - dat += "Refresh
    " - dat += "Close
    " - user << browse(dat, "window=anomaly;size=450x500") - onclose(user, "anomaly") - -obj/machinery/anomaly/attackby(obj/item/weapon/W as obj, mob/living/user as mob) - if(istype(W, /obj/item/weapon/reagent_containers/glass)) - //var/obj/item/weapon/reagent_containers/glass/G = W - if(held_container) - user << "\red You must remove the [held_container] first." - else - user << "\blue You put the [W] into the [src]." - user.drop_item(W) - held_container = W - held_container.loc = src - updateDialog() - - /*else if(istype(W, /obj/item/weapon/tank)) - //var/obj/item/weapon/reagent_containers/glass/G = W - if(fuel_container) - user << "\red You must remove the [fuel_container] first." - else - user << "\blue You put the [fuel_container] into the [src]." - user.drop_item(W) - fuel_container.loc = src - fuel_container = W - updateDialog()*/ - else - return ..() - -obj/machinery/anomaly/proc/ScanResults() - //instantiate in children to produce unique scan behaviour - return "\red Error initialising scanning components." - -obj/machinery/anomaly/proc/FinishScan() - scan_process = 0 - updateDialog() - - //determine the results and print a report - if(held_container) - src.visible_message("\blue \icon[src] makes an insistent chime.", 2) - var/obj/item/weapon/paper/P = new(src.loc) - P.name = "[src] report #[++report_num]" - P.info = "[src] analysis report #[report_num]

    " + ScanResults() - P.stamped = list(/obj/item/weapon/stamp) - P.overlays = list("paper_stamped") - else - src.visible_message("\blue \icon[src] makes a low buzzing noise.", 2) - -obj/machinery/anomaly/Topic(href, href_list) - ..() - usr.set_machine(src) - if(href_list["close"]) - usr << browse(null, "window=anomaly") - usr.machine = null - if(href_list["eject_beaker"]) - held_container.loc = src.loc - held_container = null - if(href_list["eject_fuel"]) - fuel_container.loc = src.loc - fuel_container = null - if(href_list["begin"]) - if(temperature >= 350) - var/proceed = input("Unsafe internal temperature detected, enter YES below to continue.","Warning") - if(proceed == "YES" && get_dist(src, usr) <= 1) - scan_process = 1 - else - scan_process = 1 - - updateUsrDialog() - -//whether the carrier sample matches the possible finds -//results greater than a threshold of 0.6 means a positive result -obj/machinery/anomaly/proc/GetResultSpecifity(var/datum/geosample/scanned_sample, var/carrier_name) - var/specifity = 0 - if(scanned_sample && carrier_name) - - if(scanned_sample.find_presence.Find(carrier_name)) - specifity = 0.75 * (scanned_sample.find_presence[carrier_name] / scanned_sample.total_spread) + 0.25 - else - specifity = rand(0, 0.5) - - return specifity diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_fourier_transform.dm b/code/modules/research/xenoarchaeology/machinery/analysis_fourier_transform.dm deleted file mode 100644 index 98c49b2372..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_fourier_transform.dm +++ /dev/null @@ -1,34 +0,0 @@ - -// This machine tells the distance to a nearby artifact, if there is one - -obj/machinery/anomaly/fourier_transform - name = "Fourier Transform spectroscope" - -obj/machinery/anomaly/fourier_transform/ScanResults() - var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." - - var/datum/geosample/scanned_sample - var/carrier - var/num_reagents = 0 - - for(var/datum/reagent/A in held_container.reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/analysis_sample)) - scanned_sample = R.data - else - carrier = R.id - num_reagents++ - - if(num_reagents == 2 && scanned_sample && carrier) - //all necessary components are present - var/specifity = GetResultSpecifity(scanned_sample, carrier) - var/distance = scanned_sample.artifact_distance - if(distance > 0) - distance += (2 * rand() - 1) * distance * 0.05 - results = "Fourier transform analysis on anomalous energy absorption through carrier ([carrier]) indicates source located inside emission radius ([95 * specifity]% accuracy): [distance]." - else - results = "Energy dispersion detected throughout sample consistent with background readings.
    " - if(carrier == scanned_sample.source_mineral) - results += "Warning, analysis may be contaminated by high quantities of molecular carrier present throughout sample." - - return results diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_gas_chromatography.dm b/code/modules/research/xenoarchaeology/machinery/analysis_gas_chromatography.dm deleted file mode 100644 index bd6dbeb9b3..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_gas_chromatography.dm +++ /dev/null @@ -1,39 +0,0 @@ - -// This machine shows the materials that are present - -obj/machinery/anomaly/gas_chromatography - name = "Gas Chromatography spectrometer" - -obj/machinery/anomaly/gas_chromatography/ScanResults() - var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." - - var/datum/geosample/scanned_sample - var/carrier - var/num_reagents = 0 - - for(var/datum/reagent/A in held_container.reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/analysis_sample)) - scanned_sample = R.data - else - carrier = R.id - num_reagents++ - - if(num_reagents == 2 && scanned_sample) - var/specifity = GetResultSpecifity(scanned_sample, carrier) - results = "Chromatography partitioning analysis over carrier ([carrier]) indicates the following elements present ([100 * specifity]% accuracy):

    " - - var/num_found = 0 - for(var/index=1,index <= scanned_sample.find_presence.len, index++) - var/find = scanned_sample.find_presence[index] - if(find && prob(100 * specifity)) - results += " - " + finds_as_strings[index] + "
    " - num_found++ - - if(!num_found) - results = "Chromatography partitioning results over carrier ([carrier]) to determine elemental makeup were inconclusive.
    " - - if(!carrier) - results += "
    No carrier detected, scan accuracy affected.
    " - - return results diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_hyperspectral.dm b/code/modules/research/xenoarchaeology/machinery/analysis_hyperspectral.dm deleted file mode 100644 index a070a61a5d..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_hyperspectral.dm +++ /dev/null @@ -1,55 +0,0 @@ - -obj/machinery/anomaly/hyperspectral - name = "Hyperspectral Imager" - icon = 'icons/obj/xenoarchaeology.dmi' - icon_state = "scanner" - -obj/machinery/anomaly/hyperspectral/process() - ..() - if(scan_process) - icon_state = "scanner_active" - else if(prob(10)) - icon_state = "scanner" - flick(src, "scanner_active") - -obj/machinery/anomaly/hyperspectral/ScanResults() - var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." - - var/datum/geosample/scanned_sample - var/carrier - var/num_reagents = 0 - - for(var/datum/reagent/A in held_container.reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/analysis_sample)) - scanned_sample = R.data - else - carrier = R.id - num_reagents++ - - if(num_reagents == 2 && scanned_sample && carrier) - //all necessary components are present - var/specifity = GetResultSpecifity(scanned_sample, carrier) - results = "Spectral signature over carrier ([carrier]):
    " - if(specifity <= 0.25) - //results += "
    " - results += "" - else if(specifity <= 0.5) - //results += "
    " - results += "" - else if(specifity <= 0.75) - //results += "
    " - results += "" - else - //results += "
    " - results += "" - - results += "
    " - if(scanned_sample.artifact_id) - results += "Detected energy signatures [100 * (1 - specifity)]% consistent with standard background readings.
    " - if(prob( (specifity + 0.5 * (1 - specifity)) * 100)) - results += "Anomalous exotic energy signature isolated: [scanned_sample.artifact_id]." - else - results += "Detected energy signatures [95 + 5 * (2 * rand() - 1) * (1 - specifity)]% consistent with standard background readings." - - return results diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_ion_mobility.dm b/code/modules/research/xenoarchaeology/machinery/analysis_ion_mobility.dm deleted file mode 100644 index 9b37d20df9..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_ion_mobility.dm +++ /dev/null @@ -1,47 +0,0 @@ - -// This machine shows the amount of a certain material that is present - -obj/machinery/anomaly/ion_mobility - name = "Ion Mobility Spectrometer" - desc = "A specialised, complex analysis machine." - icon = 'icons/obj/virology.dmi' - icon_state = "analyser" - -obj/machinery/anomaly/ion_mobility/ScanResults() - var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." - - var/datum/geosample/scanned_sample - var/carrier - var/num_reagents = 0 - - for(var/datum/reagent/A in held_container.reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/analysis_sample)) - scanned_sample = R.data - else - carrier = R.id - num_reagents++ - - if(num_reagents == 2 && scanned_sample && carrier) - //all necessary components are present - results = "Kinetic analysis on sample's ionic residue in carrier ([carrier]) indicates the dissonance spread:

    " - var/found = 0 - if(scanned_sample.find_presence.Find(carrier)) - var/dis_ratio = scanned_sample.find_presence[carrier] - var/desc_index = responsive_carriers.Find(carrier) - results += " - [finds_as_strings[desc_index]]: [dis_ratio]
    " - found++ - /* - for(var/index=1,index <= scanned_sample.find_presence.len, index++) - var/find = scanned_sample.find_presence[index] - //world << "index: [index], find: [find], response: [responsive_carriers[index]], carrier: [carrier]" - if(find && responsive_carriers[index] == carrier) - results += " - [finds_as_strings[index]] [find * 100]%
    " - found++ - */ - if(!found) - results = "Kinetic analysis on sample's ionic residue in carrier ([carrier]) to determine composition were inconclusive.
    " - if(carrier == scanned_sample.source_mineral) - results += "Warning, analysis may be contaminated by high quantities of molecular carrier present throughout sample." - - return results diff --git a/code/modules/research/xenoarchaeology/machinery/analysis_isotope_ratio.dm b/code/modules/research/xenoarchaeology/machinery/analysis_isotope_ratio.dm deleted file mode 100644 index 04efb278ad..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/analysis_isotope_ratio.dm +++ /dev/null @@ -1,51 +0,0 @@ - -// This machine shows the age for newer finds - -obj/machinery/anomaly/isotope_ratio - name = "Isotope ratio spectrometer" - desc = "A specialised, complex analysis machine." - icon = 'icons/obj/virology.dmi' - icon_state = "analyser" - -obj/machinery/anomaly/isotope_ratio/ScanResults() - var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." - - var/datum/geosample/scanned_sample - var/carrier_name - var/num_reagents = 0 - - for(var/datum/reagent/A in held_container.reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/analysis_sample)) - scanned_sample = R.data - else - carrier_name = R.id - num_reagents++ - - if(num_reagents == 2 && scanned_sample && carrier_name) - var/accuracy = GetResultSpecifity(scanned_sample, carrier_name) - accuracy += 0.5 * (1 - accuracy) / scanned_sample.total_spread - if(!accuracy) - accuracy = rand(0.01, 0.5) - results = "Isotope decay analysis in carrier ([carrier_name]) indicates age ([100 * accuracy]% accuracy):

    " - - if(scanned_sample.age_billion) - //scramble the results - var/displayed_age_thousands = rand(0, 999) - var/displayed_age_millions = rand(0, 999) - results += "[displayed_age_millions + displayed_age_thousands / 1000] million years.
    " - else if(scanned_sample.age_million) - var/displayed_age_thousands = scanned_sample.age_thousand + max(scanned_sample.age_thousand * ((1 - accuracy) * (2 * rand() - 1)), 0) - var/displayed_age_millions = scanned_sample.age_million + max(scanned_sample.age_million * ((1 - accuracy) * (4 * rand() - 2)), 0) - results += "[displayed_age_millions + displayed_age_thousands / 1000] million years.
    " - else if(scanned_sample.age_thousand) - var/displayed_age = scanned_sample.age + scanned_sample.age * ((1 - accuracy) * (2 * rand() - 1)) - var/displayed_age_thousands = scanned_sample.age_thousand + max(scanned_sample.age_thousand * ((1 - accuracy) * (2 * rand() - 1)), 0) - results += "[displayed_age_thousands + displayed_age / 1000] thousand years.
    " - else - var/displayed_age = scanned_sample.age + max(scanned_sample.age * ((1 - accuracy) * (2 * rand() - 1)), 0) - results += "[displayed_age] years.
    " - - results += "
    Warning, results only valid up to ages of one billion years." - - return results diff --git a/code/modules/research/xenoarchaeology/machinery/artifact_analyser_old.dm b/code/modules/research/xenoarchaeology/machinery/artifact_analyser_old.dm deleted file mode 100644 index ff1415997b..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/artifact_analyser_old.dm +++ /dev/null @@ -1,318 +0,0 @@ - -//cael - some changes here. the analysis pad is entirely new - -/obj/machinery/artifact_analyser - name = "Artifact Analyser" - desc = "Studies the structure of artifacts to discover their uses." - icon = 'icons/obj/virology.dmi' - icon_state = "analyser" - anchored = 1 - density = 1 - var/working = 0 - var/accuO = 0 - var/accuT = 0 - var/accuE1 = 0 - var/accuE2 = 0 - var/aorigin = "None" - var/atrigger = "None" - var/aeffect1 = "None" - var/aeffect2 = "None" - var/list/origin_bonuses - var/list/trigger_bonuses - var/list/function_bonuses - var/list/range_bonuses - var/cur_id = "" - var/scan_num = 0 - var/obj/machinery/artifact/cur_artifact = null - var/obj/machinery/analyser_pad/owned_pad = null - var/list/allorigins = list("Ancient Robots","Martian","Wizard Federation","Extradimensional","Precursor") - var/list/alltriggers = list("Contact with Living Organism","Heavy Impact","Contact with Energy Source","Contact with Hydrogen","Contact with Corrosive Substance","Contact with Volatile Substance","Contact with Toxins","Exposure to Heat") - var/list/alleffects = list("Healing Device","Anti-biological Weapon","Non-lethal Stunning Trap","Mechanoid Repair Module","Mechanoid Deconstruction Device","Power Generator","Power Drain","Stellar Mineral Attractor","Agriculture Regulator","Shield Generator","Space-Time Displacer") - var/list/allranges = list("Constant Short-Range Energy Field","Medium Range Energy Pulses","Long Range Energy Pulses","Extreme Range Energy Pulses","Requires contact with subject") - -/obj/machinery/artifact_analyser/New() - ..() - origin_bonuses = new/list() - origin_bonuses["ancient"] = 0 - origin_bonuses["martian"] = 0 - origin_bonuses["wizard"] = 0 - origin_bonuses["eldritch"] = 0 - origin_bonuses["precursor"] = 0 - trigger_bonuses = new/list() - trigger_bonuses["ancient"] = 0 - trigger_bonuses["martian"] = 0 - trigger_bonuses["wizard"] = 0 - trigger_bonuses["eldritch"] = 0 - trigger_bonuses["precursor"] = 0 - function_bonuses = new/list() - function_bonuses["ancient"] = 0 - function_bonuses["martian"] = 0 - function_bonuses["wizard"] = 0 - function_bonuses["eldritch"] = 0 - function_bonuses["precursor"] = 0 - range_bonuses = new/list() - range_bonuses["ancient"] = 0 - range_bonuses["martian"] = 0 - range_bonuses["wizard"] = 0 - range_bonuses["eldritch"] = 0 - range_bonuses["precursor"] = 0 - // - spawn(10) - owned_pad = locate() in orange(1, src) - -/obj/machinery/artifact_analyser/attack_hand(var/mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - user.machine = src - var/dat = "Artifact Analyser
    " - dat += "

    " - if(!owned_pad) - dat += "Unable to locate analysis pad.
    " - dat += "

    " - else if (!src.working) - dat += "Artifact ID: [cur_id]
    " - dat += "Artifact Origin: [aorigin] ([accuO]%)
    " - dat += "Activation Trigger: [atrigger] ([accuT]%)
    " - dat += "Artifact Function: [aeffect1] ([accuE1]%)
    " - dat += "Artifact Range: [aeffect2] ([accuE2]%)

    " - dat += "

    " - dat += "Artifact ID is determined from unique energy emission signatures.
    " - dat += "Analyse Artifact (Scan number #[scan_num+1])
    " - dat += "Upload/update artifact scan
    " - dat += "Print Page
    " - else - dat += "Please wait. Analysis in progress.
    " - dat += "

    " - // - dat += "Close
    " - user << browse(dat, "window=artanalyser;size=450x500") - onclose(user, "artanalyser") - -/obj/machinery/artifact_analyser/process() - if(stat & (NOPOWER|BROKEN)) - return - use_power(350) - // - if(!owned_pad) - for(var/obj/machinery/analyser_pad/pad in range(1)) - owned_pad = pad - break - -/obj/machinery/artifact_analyser/proc/AA_FailedAnalysis(var/failtype) - switch(failtype) - if(1) - src.aorigin = "Failed to Identify" - if (prob(20)) src.aorigin = pick(src.allorigins) - if(2) - src.atrigger = "Failed to Identify" - if (prob(20)) src.atrigger = pick(src.alltriggers) - if(3) - src.aeffect1 = "Failed to Identify" - if (prob(20)) src.aeffect1 = pick(src.alleffects) - if(4) - src.aeffect2 = "Failed to Identify" - if (prob(20)) src.aeffect2 = pick(src.allranges) - -/obj/machinery/artifact_analyser/proc/AA_Analyse() - if(!cur_artifact) - return - src.accuO = 5 + rand(0,10) + origin_bonuses[cur_artifact.origin] + cur_artifact.activated * 50 - src.accuT = 5 + rand(0,10) + trigger_bonuses[cur_artifact.origin] + cur_artifact.activated * 50 - src.accuE1 = 5 + rand(0,10) + function_bonuses[cur_artifact.origin] + cur_artifact.activated * 50 - src.accuE2 = 5 + rand(0,10) + range_bonuses[cur_artifact.origin] + cur_artifact.activated * 50 - - //keep any correctly determined properties the same - var/origin_correct = 0 - var/trigger_correct = 0 - var/function_correct = 0 - var/range_correct = 0 - if(cur_id == cur_artifact.display_id) - if(src.aorigin == cur_artifact.origin) - origin_correct = 1 - - if(src.atrigger == cur_artifact.my_effect.trigger) - trigger_correct = 1 - else if(src.atrigger == cur_artifact.my_effect.triggerX) - trigger_correct = 1 - - if(src.aeffect1 == cur_artifact.my_effect.effecttype) - function_correct = 1 - - if(src.aeffect2 == cur_artifact.my_effect.effectmode) - range_correct = 1 - - if (src.accuO > 100) src.accuO = 100 - if (src.accuT > 100) src.accuT = 100 - if (src.accuE1 > 100) src.accuE1 = 100 - if (src.accuE2 > 100) src.accuE2 = 100 - // Roll to generate report - if (prob(accuO) || origin_correct) - switch(cur_artifact.origin) - if("ancient") src.aorigin = "Ancient Robots" - if("martian") src.aorigin = "Martian" - if("wizard") src.aorigin = "Wizard Federation" - if("eldritch") src.aorigin = "Extradimensional" - if("precursor") src.aorigin = "Precursor" - else src.aorigin = "Unknown Origin" - origin_bonuses[cur_artifact.origin] += 10 - else - AA_FailedAnalysis(1) - origin_bonuses[cur_artifact.origin] += 5 - if (prob(accuT) || trigger_correct) - switch(cur_artifact.my_effect.trigger) - if("touch") src.atrigger = "Contact with Living Organism" - if("force") src.atrigger = "Heavy Impact" - if("energy") src.atrigger = "Contact with Energy Source" - if("chemical") - switch(cur_artifact.my_effect.triggerX) - if("hydrogen") src.atrigger = "Contact with Hydrogen" - if("corrosive") src.atrigger = "Contact with Corrosive Substance" - if("volatile") src.atrigger = "Contact with Volatile Substance" - if("toxin") src.atrigger = "Contact with Toxins" - if("heat") src.atrigger = "Exposure to Heat" - else src.atrigger = "Unknown Trigger" - trigger_bonuses[cur_artifact.origin] += 5 - else - AA_FailedAnalysis(2) - trigger_bonuses[cur_artifact.origin] += 1 - if (prob(accuE1) || function_correct) - switch(cur_artifact.my_effect.effecttype) - if("healing") src.aeffect1 = "Healing Device" - if("injure") src.aeffect1 = "Anti-biological Weapon" - // if("stun") src.aeffect1 = "Non-lethal Stunning Trap" - if("roboheal") src.aeffect1 = "Mechanoid Repair Module" - if("robohurt") src.aeffect1 = "Mechanoid Deconstruction Device" - if("cellcharge") src.aeffect1 = "Power Generator" - if("celldrain") src.aeffect1 = "Power Drain" - if("planthelper") src.aeffect1 = "Agriculture Regulator" - if("forcefield") src.aeffect1 = "Shield Generator" - if("teleport") src.aeffect1 = "Space-Time Displacer" - else src.aeffect1 = "Unknown Effect" - function_bonuses[cur_artifact.origin] += 5 - else - AA_FailedAnalysis(3) - function_bonuses[cur_artifact.origin] += 1 - if (prob(accuE2) || range_correct) - switch(cur_artifact.my_effect.effectmode) - if("aura") src.aeffect2 = "Constant Short-Range Energy Field" - if("pulse") - if(cur_artifact.my_effect.aurarange > 7) src.aeffect2 = "Long Range Energy Pulses" - else src.aeffect2 = "Medium Range Energy Pulses" - if("worldpulse") src.aeffect2 = "Extreme Range Energy Pulses" - if("contact") src.aeffect2 = "Requires contact with subject" - else src.aeffect2 = "Unknown Range" - range_bonuses[cur_artifact.origin] += 5 - else - AA_FailedAnalysis(4) - range_bonuses[cur_artifact.origin] += 1 - - cur_artifact.name = "alien artifact ([cur_artifact.display_id])" - cur_artifact.desc = "A large alien device. It has a small tag near the bottom that reads \"[cur_artifact.display_id]\"." - cur_id = cur_artifact.display_id - cur_artifact.my_effect.artifact_id = cur_artifact.display_id - -/obj/machinery/artifact_analyser/Topic(href, href_list) - - if(href_list["analyse"]) - if(owned_pad) - var/turf/pad_turf = get_turf(owned_pad) - var/findarti = 0 - for(var/obj/machinery/artifact/A in pad_turf.contents) - findarti++ - cur_artifact = A - if (findarti == 1) - if(cur_artifact && cur_artifact.being_used) - var/message = "[src] states, \"Cannot analyse. Excess energy drain is disrupting signal.\"" - src.visible_message(message, message) - else - cur_artifact.anchored = 1 - cur_artifact.being_used = 1 - src.working = 1 - src.icon_state = "analyser_processing" - var/time = rand(30,50) + max(0, 300 - scan_num * 10) - /*for(var/i = artifact_research.starting_tier, i <= artifact_research.max_tiers, i++) - for(var/datum/artiresearch/R in artifact_research.researched_items[i]) - if (R.bonustype == "analyser") time -= R.bonusTime*/ - time *= 10 - var/message = "[src] states, \"Commencing analysis.\"" - src.visible_message(message, message) - use_power(500) - spawn(time) - src.working = 0 - icon_state = "analyser" - cur_artifact.anchored = 0 - cur_artifact.being_used = 0 - if(cur_artifact.loc == pad_turf) - AA_Analyse() - scan_num++ - message = "[src] states, \"Analysis complete.\"" - src.visible_message(message, message) - use_power(500) - else if (findarti > 1) - var/message = "[src] states, \"Cannot analyse. Error isolating energy signature.\"" - src.visible_message(message, message) - else - var/message = "[src] states, \"Cannot analyse. No noteworthy energy signature isolated.\"" - src.visible_message(message, message) - - if(href_list["upload"] && cur_id != "") - //add new datum to every DB in the world - for(var/obj/machinery/computer/artifact_database/DB in world) - var/update = 0 - for(var/datum/catalogued_artifact/CA in DB.catalogued_artifacts) - if(CA.display_id == cur_id) - //already there, so update it - update = 1 - CA.origin = aorigin + " ([accuO]%)" - CA.trigger = atrigger + " ([accuT]%)" - CA.effecttype = aeffect1 + " ([accuE1]%)" - CA.effectmode = aeffect2 + " ([accuE2]%)" - if(!update) - //not there, so add it - var/datum/catalogued_artifact/CA = new() - CA.display_id = cur_id - CA.origin = aorigin + " ([accuO]%)" - CA.trigger = atrigger + " ([accuT]%)" - CA.effecttype = aeffect1 + " ([accuE1]%)" - CA.effectmode = aeffect2 + " ([accuE2]%)" - DB.catalogued_artifacts.Add(CA) - use_power(100) - - if(href_list["print"]) - var/r = "Artifact Analysis Report (Scan #[scan_num])
    " - r += "Artifact ID: [cur_id] (determined from unique energy emission signatures)
    " - r += "Artifact Origin: [aorigin] ([accuO]%)
    " - r += "Activation Trigger: [atrigger] ([accuT]%)
    " - r += "Artifact Function: [aeffect1] ([accuE1]%)
    " - r += "Artifact Range: [aeffect2] ([accuE2]%)

    " - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(src.loc) - P.name = "Artifact Analysis Report #[scan_num]" - P.info = r - for(var/mob/O in hearers(src, null)) - O.show_message("\icon[src] \blue The [src.name] prints a sheet of paper", 3) - use_power(10) - - if(href_list["close"]) - usr << browse(null, "window=artanalyser") - usr.machine = null - - src.updateDialog() - -//stick artifacts onto this then switch the analyser on -/obj/machinery/analyser_pad - name = "artifact analysis pad" - desc = "Studies the structure of artifacts to discover their uses." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "tele0" - anchored = 1 - density = 0 - -/obj/machinery/analyser_pad/New() - ..() - /*spawn(10) - for(var/obj/machinery/artifact_analyser/analyser in orange(1)) - world << "pad found analyser" - if(!analyser.owned_pad) - analyser.owned_pad = src - world << "pad set analyser to self" - break*/ diff --git a/code/modules/research/xenoarchaeology/machinery/artifact_db_old.dm b/code/modules/research/xenoarchaeology/machinery/artifact_db_old.dm deleted file mode 100644 index c6e0d1fb3f..0000000000 --- a/code/modules/research/xenoarchaeology/machinery/artifact_db_old.dm +++ /dev/null @@ -1,58 +0,0 @@ - -/datum/catalogued_artifact - var/trigger = "touch" // What activates it? - var/effecttype = "healing" // What does it do? - var/effectmode = "aura" // How does it carry out the effect? - var/display_id = "" // Artifact ID to display once successfully scanned - var/origin = "" - -/obj/machinery/computer/artifact_database - name = "Artifact Database" - icon_state = "rdcomp" - var/list/catalogued_artifacts - -/obj/machinery/computer/artifact_database/New() - ..() - catalogued_artifacts = new/list - -/obj/machinery/computer/artifact_database/Topic(href, href_list) - ..() - if( href_list["close"] ) - usr << browse(null, "window=artifact_db") - usr.machine = null - updateDialog() - -/obj/machinery/computer/artifact_database/process() - ..() - updateDialog() - -/obj/machinery/computer/artifact_database/interact(mob/user) - if ( (get_dist(src, user) > 1 ) || (stat & (BROKEN|NOPOWER)) ) - if (!istype(user, /mob/living/silicon)) - user.machine = null - user << browse(null, "window=artifact_db") - return - var/t = "Artifact Database
    " - t += "
    " - for(var/datum/catalogued_artifact/CA in catalogued_artifacts) - t += "Artifact ID: [CA.display_id] (determined from unique energy emission signatures)
    " - t += "Artifact Origin: [CA.origin]
    " - t += "Activation Trigger: [CA.trigger]
    " - t += "Artifact Function: [CA.effecttype]
    " - t += "Artifact Range: [CA.effectmode]

    " - t += "
    " - t += "
    Refresh Close
    " - user << browse(t, "window=artifact_db;size=500x800") - user.machine = src - -/* -/datum/artifact_effect - var/origin = null // Used in the randomisation/research of the artifact. - var/trigger = "touch" // What activates it? - var/triggerX = "none" // Used for more varied triggers - var/effecttype = "healing" // What does it do? - var/effectmode = "aura" // How does it carry out the effect? - var/aurarange = 4 // How far the artifact will extend an aura effect. - var/display_id = "" // Artifact ID to display once successfully scanned - var/list/created_field -*/ \ No newline at end of file diff --git a/code/modules/research/xenoarchaeology/machinery/coolant.dm b/code/modules/research/xenoarchaeology/machinery/coolant.dm new file mode 100644 index 0000000000..bc03553bee --- /dev/null +++ b/code/modules/research/xenoarchaeology/machinery/coolant.dm @@ -0,0 +1,59 @@ + +datum/reagent/coolant + name = "Coolant" + id = "coolant" + description = "Industrial cooling substance." + reagent_state = LIQUID + color = "#C8A5DC" // rgb: 200, 165, 220 + +datum/chemical_reaction/coolant + name = "Coolant" + id = "coolant" + result = "coolant" + required_reagents = list("tungsten" = 1, "oxygen" = 1, "water" = 1) + result_amount = 3 + + + +/obj/structure/reagent_dispensers/coolanttank + name = "coolant tank" + desc = "A tank of industrial coolant" + icon = 'icons/obj/objects.dmi' + icon_state = "coolanttank" + amount_per_transfer_from_this = 10 + New() + ..() + reagents.add_reagent("coolant",1000) + +/obj/structure/reagent_dispensers/coolanttank/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet)) + if(!istype(Proj ,/obj/item/projectile/beam/lastertag) && !istype(Proj ,/obj/item/projectile/beam/practice) ) + explode() + +/obj/structure/reagent_dispensers/coolanttank/blob_act() + explode() + +/obj/structure/reagent_dispensers/coolanttank/ex_act() + explode() + +/obj/structure/reagent_dispensers/coolanttank/proc/explode() + var/datum/effect/effect/system/harmless_smoke_spread/S = new /datum/effect/effect/system/harmless_smoke_spread + //S.attach(src) + S.set_up(5, 0, src.loc) + + playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) + spawn(0) + S.start() + + var/datum/gas_mixture/env = src.loc.return_air() + if(env) + if (reagents.total_volume > 750) + env.temperature = 0 + else if (reagents.total_volume > 500) + env.temperature -= 100 + else + env.temperature -= 50 + + sleep(10) + if(src) + del(src) diff --git a/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm b/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm new file mode 100644 index 0000000000..6dcd45c825 --- /dev/null +++ b/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm @@ -0,0 +1,363 @@ + +/obj/machinery/radiocarbon_spectrometer + name = "Radiocarbon spectrometer" + desc = "A specialised, complex scanner for gleaning information on all manner of small things." + anchored = 1 + density = 1 + icon = 'icons/obj/virology.dmi' + icon_state = "analyser" + + use_power = 1 //1 = idle, 2 = active + idle_power_usage = 20 + active_power_usage = 300 + + //var/obj/item/weapon/reagent_containers/glass/coolant_container + var/scanning = 0 + var/report_num = 0 + // + var/obj/item/scanned_item + var/last_scan_data = "No scans on record." + // + var/last_process_worldtime = 0 + // + var/scanner_progress = 0 + var/scanner_rate = 1.25 //80 seconds per scan + var/scanner_rpm = 0 + var/scanner_rpm_dir = 1 + var/scanner_temperature = 0 + var/scanner_seal_integrity = 100 + // + var/coolant_usage_rate = 0 //measured in u/microsec + var/fresh_coolant = 0 + var/coolant_purity = 0 + var/datum/reagents/coolant_reagents + var/used_coolant = 0 + var/list/coolant_reagents_purity = list() + // + var/maser_wavelength = 0 + var/optimal_wavelength = 0 + var/optimal_wavelength_target = 0 + var/tleft_retarget_optimal_wavelength = 0 + var/maser_efficiency = 0 + // + var/radiation = 0 //0-100 mSv + var/t_left_radspike = 0 + var/rad_shield = 0 + +/obj/machinery/radiocarbon_spectrometer/New() + ..() + create_reagents(500) + coolant_reagents_purity["water"] = 0.5 + coolant_reagents_purity["icecoffee"] = 0.6 + coolant_reagents_purity["icetea"] = 0.6 + coolant_reagents_purity["milkshake"] = 0.6 + coolant_reagents_purity["leporazine"] = 0.7 + coolant_reagents_purity["kelotane"] = 0.7 + coolant_reagents_purity["sterilizine"] = 0.7 + coolant_reagents_purity["dermaline"] = 0.7 + coolant_reagents_purity["hyperzine"] = 0.8 + coolant_reagents_purity["cryoxadone"] = 0.9 + coolant_reagents_purity["coolant"] = 1 + coolant_reagents_purity["adminordrazine"] = 2 + +/obj/machinery/radiocarbon_spectrometer/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/radiocarbon_spectrometer/attackby(var/obj/I as obj, var/mob/user as mob) + if(scanning) + user << "You can't do that while [src] is scanning!" + else + if(istype(I, /obj/item/stack/nanopaste)) + var/choice = alert("What do you want to do with the nanopaste?","Radiometric Scanner","Scan nanopaste","Fix seal integrity") + if(choice == "Fix seal integrity") + var/obj/item/stack/nanopaste/N = I + var/amount_used = min(N.amount, 10 - scanner_seal_integrity / 10) + N.use(amount_used) + scanner_seal_integrity = round(scanner_seal_integrity + amount_used * 10) + return + if(istype(I, /obj/item/weapon/reagent_containers/glass)) + var/choice = alert("What do you want to do with the container?","Radiometric Scanner","Add coolant","Empty coolant","Scan container") + if(choice == "Add coolant") + var/obj/item/weapon/reagent_containers/glass/G = I + var/amount_transferred = min(src.reagents.maximum_volume - src.reagents.total_volume, G.reagents.total_volume) + G.reagents.trans_to(src, amount_transferred) + user << "You empty [amount_transferred]u of coolant into [src]." + update_coolant() + return + else if(choice == "Empty coolant") + var/obj/item/weapon/reagent_containers/glass/G = I + var/amount_transferred = min(G.reagents.maximum_volume - G.reagents.total_volume, src.reagents.total_volume) + src.reagents.trans_to(G, amount_transferred) + user << "You remove [amount_transferred]u of coolant from [src]." + update_coolant() + return + user.drop_item() + I.loc = src + scanned_item = I + +/obj/machinery/radiocarbon_spectrometer/proc/update_coolant() + var/total_purity = 0 + fresh_coolant = 0 + coolant_purity = 0 + var/num_reagent_types = 0 + for (var/datum/reagent/current_reagent in src.reagents.reagent_list) + if (!current_reagent) + continue + var/cur_purity = coolant_reagents_purity[current_reagent.id] + if(!cur_purity) + cur_purity = 0.1 + else if(cur_purity > 1) + cur_purity = 1 + total_purity += cur_purity * current_reagent.volume + fresh_coolant += current_reagent.volume + num_reagent_types += 1 + if(total_purity && fresh_coolant) + coolant_purity = total_purity / fresh_coolant + +/obj/machinery/radiocarbon_spectrometer/ui_interact(mob/user, ui_key = "radio_spectro") + + if(user.stat) + return + + // this is the data which will be sent to the ui + var/data[0] + data["scanned_item"] = (scanned_item ? scanned_item.name : "") + data["scanned_item_desc"] = (scanned_item ? (scanned_item.desc ? scanned_item.desc : "No information on record.") : "") + data["last_scan_data"] = last_scan_data + // + data["scan_progress"] = round(scanner_progress) + data["scanning"] = scanning + // + data["scanner_seal_integrity"] = round(scanner_seal_integrity) + data["scanner_rpm"] = round(scanner_rpm) + data["scanner_temperature"] = round(scanner_temperature) + // + data["coolant_usage_rate"] = "[coolant_usage_rate]" + data["unused_coolant_abs"] = round(fresh_coolant) + data["unused_coolant_per"] = round(fresh_coolant / reagents.maximum_volume * 100) + data["coolant_purity"] = "[coolant_purity * 100]" + // + data["optimal_wavelength"] = round(optimal_wavelength) + data["maser_wavelength"] = round(maser_wavelength) + data["maser_efficiency"] = round(maser_efficiency * 100) + // + data["radiation"] = round(radiation) + data["t_left_radspike"] = round(t_left_radspike) + data["rad_shield_on"] = rad_shield + + var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, ui_key) + if (!ui) + // the ui does not exist, so we'll create a new one + ui = new(user, src, ui_key, "geoscanner.tmpl", "High Res Radiocarbon Spectrometer", 900, 825) + // When the UI is first opened this is the data it will use + ui.set_initial_data(data) + ui.open() + // Auto update every Master Controller tick + ui.set_auto_update(1) + else + // The UI is already open so push the new data to it + ui.push_data(data) + return + +/obj/machinery/radiocarbon_spectrometer/process() + if(scanning) + if(!scanned_item || scanned_item.loc != src) + scanned_item = null + stop_scanning() + else if(scanner_progress >= 100) + complete_scan() + else + //calculate time difference + var/deltaT = (world.time - last_process_worldtime) * 0.1 + + //modify the RPM over time + //i want 1u to last for 10 sec at 500 RPM, scaling linearly + scanner_rpm += scanner_rpm_dir * 50 * deltaT + if(scanner_rpm > 1000) + scanner_rpm = 1000 + scanner_rpm_dir = -1 * pick(0.5, 2.5, 5.5) + else if(scanner_rpm < 1) + scanner_rpm = 1 + scanner_rpm_dir = 1 * pick(0.5, 2.5, 5.5) + + //heat up according to RPM + //each unit of coolant + scanner_temperature += scanner_rpm * deltaT * 0.05 + + //radiation + t_left_radspike -= deltaT + if(t_left_radspike > 0) + //ordinary radiation + radiation = rand() * 15 + else + //radspike + if(t_left_radspike > -5) + radiation = rand() * 15 + 85 + if(!rad_shield) + //irradiate nearby mobs + for(var/mob/living/M in view(7,src)) + M.apply_effect(radiation / 25, IRRADIATE, 0) + else + t_left_radspike = pick(10,15,25) + + //use some coolant to cool down + if(coolant_usage_rate > 0) + var/coolant_used = min(fresh_coolant, coolant_usage_rate * deltaT) + if(coolant_used > 0) + fresh_coolant -= coolant_used + used_coolant += coolant_used + scanner_temperature = max(scanner_temperature - coolant_used * coolant_purity * 20, 0) + + //modify the optimal wavelength + tleft_retarget_optimal_wavelength -= deltaT + if(tleft_retarget_optimal_wavelength <= 0) + tleft_retarget_optimal_wavelength = pick(4,8,15) + optimal_wavelength_target = rand() * 9900 + 100 + // + if(optimal_wavelength < optimal_wavelength_target) + optimal_wavelength = min(optimal_wavelength + 700 * deltaT, optimal_wavelength_target) + else if(optimal_wavelength > optimal_wavelength_target) + optimal_wavelength = max(optimal_wavelength - 700 * deltaT, optimal_wavelength_target) + // + maser_efficiency = 1 - max(min(10000, abs(optimal_wavelength - maser_wavelength) * 3), 1) / 10000 + + //make some scan progress + if(!rad_shield) + scanner_progress = min(100, scanner_progress + scanner_rate * maser_efficiency * deltaT) + + //degrade the seal over time according to temperature + //i want temperature of 50K to degrade at 1%/sec + scanner_seal_integrity -= (max(scanner_temperature, 1) / 1000) * deltaT + + //emergency stop if seal integrity reaches 0 + if(scanner_seal_integrity <= 0 || (scanner_temperature >= 1273 && !rad_shield)) + stop_scanning() + src.visible_message("\blue \icon[src] buzzes unhappily. It has failed mid-scan!", 2) + + if(prob(5)) + src.visible_message("\blue \icon[src] [pick("whirrs","chuffs","clicks")][pick(" excitedly"," energetically"," busily")].", 2) + else + //gradually cool down over time + if(scanner_temperature > 0) + scanner_temperature = max(scanner_temperature - 5 - 10 * rand(), 0) + if(prob(0.75)) + src.visible_message("\blue \icon[src] [pick("plinks","hisses")][pick(" quietly"," softly"," sadly"," plaintively")].", 2) + last_process_worldtime = world.time + +/obj/machinery/radiocarbon_spectrometer/proc/stop_scanning() + scanning = 0 + scanner_rpm_dir = 1 + scanner_rpm = 0 + optimal_wavelength = 0 + maser_efficiency = 0 + maser_wavelength = 0 + coolant_usage_rate = 0 + radiation = 0 + t_left_radspike = 0 + if(used_coolant) + src.reagents.remove_any(used_coolant) + used_coolant = 0 + +/obj/machinery/radiocarbon_spectrometer/proc/complete_scan() + src.visible_message("\blue \icon[src] makes an insistent chime.", 2) + + if(scanned_item) + //create report + var/obj/item/weapon/paper/P = new(src) + P.name = "[src] report #[++report_num]: [scanned_item.name]" + P.stamped = list(/obj/item/weapon/stamp) + P.overlays = list("paper_stamped") + + //work out data + var/data = " - Mundane object: [scanned_item.desc ? scanned_item.desc : "No information on record."]
    " + var/datum/geosample/G + switch(scanned_item.type) + if(/obj/item/weapon/ore) + var/obj/item/weapon/ore/O = scanned_item + if(O.geological_data) + G = O.geological_data + + if(/obj/item/weapon/rocksliver) + var/obj/item/weapon/rocksliver/O = scanned_item + if(O.geological_data) + G = O.geological_data + + if(/obj/item/weapon/archaeological_find) + data = " - Mundane object (archaic xenos origins)
    " + + var/obj/item/weapon/archaeological_find/A = scanned_item + if(A.speaking_to_players) + data = " - Exhibits properties consistent with sonic reproduction.
    " + if(A.listening_to_players) + data = " - Exhibits properties similar to audio capture technology.
    " + + var/anom_found = 0 + if(G) + data = " - Spectometric analysis on mineral sample has determined type [finds_as_strings[responsive_carriers.Find(G.source_mineral)]]
    " + if(G.age_billion > 0) + data += " - Radiometric dating shows age of [G.age_billion].[G.age_million] billion years
    " + else if(G.age_million > 0) + data += " - Radiometric dating shows age of [G.age_million].[G.age_thousand] million years
    " + else + data += " - Radiometric dating shows age of [G.age_thousand * 1000 + G.age] years
    " + data += " - Chromatographic analysis shows the following materials present:
    " + for(var/carrier in G.find_presence) + if(G.find_presence[carrier]) + var/index = responsive_carriers.Find(carrier) + if(index > 0 && index <= finds_as_strings.len) + data += " > [100 * G.find_presence[carrier]]% [finds_as_strings[index]]
    " + + if(G.artifact_id && G.artifact_distance >= 0) + anom_found = 1 + data += " - Hyperspectral imaging reveals exotic energy wavelength detected with ID: [G.artifact_id]
    " + data += " - Fourier transform analysis on anomalous energy absorption indicates energy source located inside emission radius of [G.artifact_distance]m
    " + + if(!anom_found) + data += " - No anomalous data
    " + + P.info = "[src] analysis report #[report_num]
    " + P.info += "Scanned item: [scanned_item.name]

    " + data + last_scan_data = P.info + P.loc = src.loc + + scanned_item.loc = src.loc + scanned_item = null + +/obj/machinery/radiocarbon_spectrometer/Topic(href, href_list) + if(stat & (NOPOWER|BROKEN)) + return 0 // don't update UIs attached to this object + + if(href_list["scanItem"]) + if(scanning) + stop_scanning() + else + if(scanned_item) + if(scanner_seal_integrity > 0) + scanner_progress = 0 + scanning = 1 + t_left_radspike = pick(5,10,15) + usr << "Scan initiated." + else + usr << "Could not initiate scan, seal requires replacing." + else + usr << "Insert an item to scan." + + if(href_list["maserWavelength"]) + maser_wavelength = max(min(maser_wavelength + 1000 * text2num(href_list["maserWavelength"]), 10000), 1) + + if(href_list["coolantRate"]) + coolant_usage_rate = max(min(coolant_usage_rate + text2num(href_list["coolantRate"]), 10000), 0) + + if(href_list["toggle_rad_shield"]) + if(rad_shield) + rad_shield = 0 + else + rad_shield = 1 + + if(href_list["ejectItem"]) + if(scanned_item) + scanned_item.loc = src.loc + scanned_item = null + + add_fingerprint(usr) + return 1 // update UIs attached to this object diff --git a/code/modules/research/xenoarchaeology/manuals.dm b/code/modules/research/xenoarchaeology/manuals.dm index ceaa3d036e..99be3a6849 100644 --- a/code/modules/research/xenoarchaeology/manuals.dm +++ b/code/modules/research/xenoarchaeology/manuals.dm @@ -49,6 +49,8 @@
  51. Core sampler - use this to take core samples from rock faces, which you can then run to the lab for analysis.
  52. Depth scanner - uses x-ray diffraction to locate anomalous densities in rock, indicating archaeological deposits or mineral veins. Comes with a handy reference log containing co-ordinates and time of each scan.
  53. +
  54. Alden-Saraspova counter - uses a patented application of Fourier Transform analysis to determine the difference between background and + exotic radiation. Use it to determine how far you are from anomalous energy sources.
  55. Radio beacon locater - leave a beacon at an item of interest, then track it down later with this handy gadget. Watch for interference from other devices though.
  56. Flashlight or portable light source - Self explanatory, I hope.
  57. @@ -129,51 +131,50 @@

    Contents

    1. A note on terms
    2. -
    3. Isotope ratio spectrometer
    4. -
    5. Accelerator spectrometer
    6. -
    7. Gas chromatography spectrometer
    8. -
    9. Ion mobility spectrometer
    10. +
    11. Analysis progression
    12. +
    13. Heat management
    14. +
    15. Ambient radiation

    A note on terms

    -
  58. Dissonance ratio - This is a pseudoarbitrary value indicating the overal presence of a particular element in a greater composite. - It takes into account volume, density, molecular excitation and isotope spread.
  59. Mass spectrometry - MS is the procedure used used to measure and quantify the components of matter. The most prized tool in the field of 'Materials analysis'
  60. Radiometric dating - MS applied using the right carrier reagents can be used to accurately determine the age of a sample.
  61. -
  62. Sample specifity - A pseudoarbitrary value used to indicate how well a sample resonates with the employed carrier reagent. Great specifity - (material resonance) indicates that there is much of the carrier reagent present in the sample.
  63. +
  64. Dissonance ratio - This is a pseudoarbitrary value indicating the overal presence of a particular element in a greater composite. + It takes into account volume, density, molecular excitation and isotope spread.
  65. +
  66. Vacuum seal integrity - A reference to how close an airtight seal is to failure.

  67. Contents -

    Isotope ratio spectrometer

    - Isotope ratio mass spectrometers work by coating a small surface with a semiliquid stationary phase consisting of the sample to be - analysed, and recording it's interactions with a gaseous mobile phase comprised of an inert or nonreactive gas such as helium or nitrogen.
    +

    Analysis progression

    + Modern mass spectrometry requires constant attention from the diligant researcher in order to be successul. There are many different elements to juggle, + and later chapters will delve into them. For the spectrometry assistant, the first thing you need to know is that the scanner wavelength is automatically + calculated for you. Just tweak the settings and try to match it with the actual wavelength as closely as possible.

    - IRMS are employed as radiometric daters, extremely accurate but only so up to ages of one billion years.
    Contents -

    Accelerator spectrometer

    - The accelerator mass spectrometer works by accelerating ions to extraordinarily high kinetic energies before mass analysis. The special strength of AMS is - isolate rare or low-strength isotopes, making it able to determine much greater ages with reasonable accuracy.
    +

    Seal integrity

    + In order to maintain sterile and environmentally static procedures, a special chamber is setup inside the spectrometer. It's protected by a proprietary vacuum seal + produced by top tier industrial science. It will only last for a certain number of scans before failing outright, but it can be resealed through use of nanite paste. + Unfortunately, it's susceptible to malforming under heat stress so exposing it to higher temperatures will cause it's operation life to drop significantly.

    - AMS are employed as extreme age radiometric daters, able to determine the age of the sample on a scale of billions of years. - They are commonly located in geology and archaeology laboratories.
    Contents -

    Gas chromatography spectrometer

    - Gas-liquid chromatography mass spectrometers work by coating a small surface with a semiliquid stationary phase consisting of the sample to be - analysed, and recording it's interactions with a gaseous mobile phase comprised of an inert or nonreactive gas such as helium or nitrogen.
    +

    Heat management

    + The scanner relies on a gyro-rotational system that varies in speed and intensity. Over the course of an ordinary scan, the RPMs can change dramatically. Higher RPMs + means greater heat generation, but is necessary for the ongoing continuation of the scan. To offset heat production, spectrometers have an inbuilt cooling system. + Researchers can modify the flow rate of coolant to aid in dropping temperature as necessary, but are advised that frequent coolant replacements may be necessary + depending on coolant purity. Water and substances such as cryoxadone are viable substitutes, but nowhere near as effective as pure coolant itself.

    - GLCS are employed in forensic and geological analysis to determine what elements are present in a sample.
    Contents -

    Ion mobility spectrometer

    - Ion mobility mass spectrometers work by examining the mobility of ionized molecules in an inert carrier gas
    +

    Ambient radiation

    + Researchers are warned that while operational, mass spectrometers emit period bursts of radiation and are thus advised to wear protective gear. In the event of + radiation spikes, there is also a special shield that can be lowered to block emissions. Lowering this, however, will have the effect of blocking the scanner + so use it sparingly.

    - IMS returns a dissonance ratio over the scanned sample and carrier reagent, indicating the average total presence of the sample.
    Contents @@ -197,67 +198,17 @@ -

    Contents

    -
      -
    1. Some useful phrases for you
    2. -
    3. Sample preparation and analysis
    4. -
    5. Fourier transform spectroscope
    6. -
    7. Hyperspectral Imager
    8. -
    - -
    -

    Some useful phrases for you

    - -
  68. Spectroscopy - Spectroscopy is the study of the behaviour of light, commonly used in the 26th century for analysis of anomalous - behaviour of energy or light.
  69. -
  70. Sample specifity - A pseudoarbitrary value used to indicate how well a sample resonates with the employed carrier reagent. Great specifity - (material resonance) indicates that there is much of the carrier reagent present in the sample.
  71. -
  72. Anomalies - Inexplicable or uncategorised occurrences in the cosmos. A fascinating and dangerous study is made to determine the function of - these rare finds, and the term is often applied to describe technology left behind by vastly superior ancient alien forerunners.
  73. -

    - Contents - -

    Sample preparation and analysis

    - When you are readying your spectrometry lab for analysis, you'll need to make sure the sample is in a form the machines can glean data from. - -
  74. Obtain material sample - This should be an ordinary chunk of matter the size of your finger, a good example is a 6mm rock core.
  75. -
  76. Run density separation treatment - Perform the DST procedure on your sample, following generic specifications.
  77. -
  78. Ensure sample purity - DST can sometimes leave behind chemical waste or chunks of matter, make sure there aren't any before proceeding.
  79. -
  80. Prepare analysis tray - A sample tray holds a miniscule amount of liquid (2u), but that's all that our spectrometers require for a good reading.
  81. -
  82. Choose carrier reagent - Standard spectrometers require 1u of the material sample, and 1u of a 'carrier' reagent to provide control comparison - and to enable refraction inferencing.
  83. -
  84. Insert sample tray into machine - And press the 'Go' button. Now go make a cup of coffee.
  85. -
  86. Monitor machine heat levels - The upper end mass spectrometers have quite complex internals, and have a tendency to critically overheat. - Make sure the heat limit isn't exceeded, or there may be potentially disastrous consequences.
  87. -
  88. Examine analysis report - it won't always make sense or provide the information you hoped for, but if you've been careful during DST and ensured - sample integrity, then there's always something to be learnt. Just don't lose the paperwork!
  89. -

    - Contents - -

    Fourier transform spectroscope

    - The FTS measures temporal coherence of radiating energy, then applies time-and-space domain measurements on the collected emission data. The collective - procedure is known as the Fourier Transform Procedure, with the mathematical algorithms dating back to the 19th century on Earth.
    -
    - As well as providing background energy readings, an FTS calculates the approximate distance and direction towards any anomalous energy signatures from - the location the scanned sample was taken from.
    - Contents - -

    Hyperspectral Imager

    - The imager scans and collates spectral energy signatures from across the electromagnetic spectrum. The collected data is then presented to the viewer in - graph form, with any anomalous (uncatalogued or unidentified) energy signatures highlighted.
    -
    - As well as visualising background energy readings, a hyperspectral imager will isolate and identify any anomalous energy signatures in the sample.
    - Contents - - - + It's perhaps one of the most exciting times to be alive, with the recent breakthroughs in understanding and categorisation of things we may one day no longer call + 'anomalies,' but rather 'infrequent or rare occurrences of certain celestial weather or phenomena.' Perhaps a little more long winded, but no less eloquent all the + same! Why, look at the strides we're making in piercing the walls of bluespace or our steadily improving ability to clarify and stabilise subspace emissions; it's + certainly an exciting time to be alive. For the moment the Hydrolian hasn't seen two spatial anomalies alike but the day will come and it is soon, I can feel it. "} /obj/item/weapon/book/manual/materials_chemistry_analysis - name = "Chemical preparation for materials analysis" + name = "Materials analysis and the chemical implications" icon_state = "chemistry" author = "Jasper Pascal, Senior Lecturer in Materials Analysis at the University of Jol'Nar" - title = "Chemical preparation for materials analysis" + title = "Materials analysis and the chemical implications" dat = {"