diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm index d6bde85833d..c709a2bb07b 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -95,6 +95,7 @@ #define CAT_SPAGHETTI "Spaghettis" #define CAT_ICE "Frozen" #define CAT_DRINK "Drinks" +#define CAT_CHEMISTRY "Chemistry" //rcd modes #define RCD_FLOORWALL 0 diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 63ee08482db..e01e5474db9 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -194,6 +194,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isgun(A) (istype(A, /obj/item/gun)) +#define is_reagent_container(O) (istype(O, /obj/item/reagent_containers)) + //Assemblies #define isassembly(O) (istype(O, /obj/item/assembly)) diff --git a/code/__HELPERS/reagents.dm b/code/__HELPERS/reagents.dm index 92c7b872bb1..b8e77066e27 100644 --- a/code/__HELPERS/reagents.dm +++ b/code/__HELPERS/reagents.dm @@ -89,3 +89,38 @@ return for(var/mob/M in viewers(5, location)) to_chat(M, notification) + +#define CONVERT_PH_TO_COLOR(pH, color) \ + switch(pH) {\ + if(14 to INFINITY)\ + { color = "#462c83" }\ + if(13 to 14)\ + { color = "#63459b" }\ + if(12 to 13)\ + { color = "#5a51a2" }\ + if(11 to 12)\ + { color = "#3853a4" }\ + if(10 to 11)\ + { color = "#3f93cf" }\ + if(9 to 10)\ + { color = "#0bb9b7" }\ + if(8 to 9)\ + { color = "#23b36e" }\ + if(7 to 8)\ + { color = "#3aa651" }\ + if(6 to 7)\ + { color = "#4cb849" }\ + if(5 to 6)\ + { color = "#b5d335" }\ + if(4 to 5)\ + { color = "#f7ec1e" }\ + if(3 to 4)\ + { color = "#fbc314" }\ + if(2 to 3)\ + { color = "#f26724" }\ + if(1 to 2)\ + { color = "#ef1d26" }\ + if(-INFINITY to 1)\ + { color = "#c6040c" }\ + } + diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index 1c8cb08a44c..6ed2fb59763 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -132,6 +132,7 @@ GLOBAL_LIST_INIT(common_loot, list( //common: basic items /obj/item/reagent_containers/glass/beaker = 1, /obj/item/reagent_containers/glass/rag = 1, /obj/item/reagent_containers/hypospray/medipen/pumpup = 2, + /obj/item/reagent_containers/glass/bottle/random_buffer = 2, ) = 1, list(//food diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index c29f20dd4f4..75f4ec2f3c4 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -68,8 +68,10 @@ */ /datum/component/personal_crafting/proc/check_contents(atom/a, datum/crafting_recipe/R, list/contents) var/list/item_instances = contents["instances"] + var/list/machines = contents["machinery"] contents = contents["other"] + var/list/requirements_list = list() // Process all requirements @@ -100,6 +102,10 @@ if(contents[requirement_path] < R.chem_catalysts[requirement_path]) return FALSE + for(var/machinery_path in R.machinery) + if(!machines[machinery_path])//We don't care for volume with machines, just if one is there or not + return FALSE + return R.check_requirements(a, requirements_list) /datum/component/personal_crafting/proc/get_environment(atom/a, list/blacklist = null, radius_range = 1) @@ -119,24 +125,27 @@ .["tool_behaviour"] = list() .["other"] = list() .["instances"] = list() - for(var/obj/item/I in get_environment(a,blacklist)) - if(.["instances"][I.type]) - .["instances"][I.type] += I - else - .["instances"][I.type] = list(I) - if(istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - .["other"][I.type] += S.amount - else if(I.tool_behaviour) - .["tool_behaviour"] += I.tool_behaviour - .["other"][I.type] += 1 - else - if(istype(I, /obj/item/reagent_containers)) - var/obj/item/reagent_containers/RC = I - if(RC.is_drainable()) - for(var/datum/reagent/A in RC.reagents.reagent_list) - .["other"][A.type] += A.volume - .["other"][I.type] += 1 + .["machinery"] = list() + for(var/obj/object in get_environment(a, blacklist)) + if(isitem(object)) + var/obj/item/item = object + LAZYADDASSOCLIST(.["instances"], item.type, item) + if(isstack(item)) + var/obj/item/stack/stack = item + .["other"][item.type] += stack.amount + else if(item.tool_behaviour) + .["tool_behaviour"] += item.tool_behaviour + .["other"][item.type] += 1 + else + if(is_reagent_container(item)) + var/obj/item/reagent_containers/container = item + if(container.is_drainable()) + for(var/datum/reagent/reagent in container.reagents.reagent_list) + .["other"][reagent.type] += reagent.volume + .["other"][item.type] += 1 + else if (ismachinery(object)) + LAZYADDASSOCLIST(.["machinery"], object.type, object) + /// Returns a boolean on whether the tool requirements of the input recipe are satisfied by the input source and surroundings. @@ -166,7 +175,7 @@ if(present_qualities[required_quality]) continue return FALSE - + for(var/required_path in recipe.tool_paths) var/found_this_tool = FALSE for(var/tool_path in available_tools) @@ -177,7 +186,7 @@ if(found_this_tool) continue return FALSE - + return TRUE @@ -233,17 +242,24 @@ . = list() var/data var/amt + var/list/requirements = list() + if(R.reqs) + requirements += R.reqs + if(R.machinery) + requirements += R.machinery main_loop: - for(var/A in R.reqs) - amt = R.reqs[A] + for(var/path_key in requirements) + amt = R.reqs[path_key] || R.machinery[path_key] + if(!amt)//since machinery can have 0 aka CRAFTING_MACHINERY_USE - i.e. use it, don't consume it! + continue main_loop surroundings = get_environment(a, R.blacklist) surroundings -= Deletion - if(ispath(A, /datum/reagent)) - var/datum/reagent/RG = new A + if(ispath(path_key, /datum/reagent)) + var/datum/reagent/RG = new path_key var/datum/reagent/RGNT while(amt > 0) var/obj/item/reagent_containers/RC = locate() in surroundings - RG = RC.reagents.get_reagent(A) + RG = RC.reagents.get_reagent(path_key) if(RG) if(!locate(RG.type) in Deletion) Deletion += new RG.type() @@ -267,11 +283,11 @@ SEND_SIGNAL(RC.reagents, COMSIG_REAGENTS_CRAFTING_PING) // - [] TODO: Make this entire thing less spaghetti else surroundings -= RC - else if(ispath(A, /obj/item/stack)) + else if(ispath(path_key, /obj/item/stack)) var/obj/item/stack/S var/obj/item/stack/SD while(amt > 0) - S = locate(A) in surroundings + S = locate(path_key) in surroundings if(S.amount >= amt) if(!locate(S.type) in Deletion) SD = new S.type() @@ -292,34 +308,34 @@ else var/atom/movable/I while(amt > 0) - I = locate(A) in surroundings + I = locate(path_key) in surroundings Deletion += I surroundings -= I amt-- var/list/partlist = list(R.parts.len) for(var/M in R.parts) partlist[M] = R.parts[M] - for(var/A in R.parts) - if(istype(A, /datum/reagent)) - var/datum/reagent/RG = locate(A) in Deletion - if(RG.volume > partlist[A]) - RG.volume = partlist[A] + for(var/part in R.parts) + if(istype(part, /datum/reagent)) + var/datum/reagent/RG = locate(part) in Deletion + if(RG.volume > partlist[part]) + RG.volume = partlist[part] . += RG Deletion -= RG continue - else if(istype(A, /obj/item/stack)) - var/obj/item/stack/ST = locate(A) in Deletion - if(ST.amount > partlist[A]) - ST.amount = partlist[A] + else if(istype(part, /obj/item/stack)) + var/obj/item/stack/ST = locate(part) in Deletion + if(ST.amount > partlist[part]) + ST.amount = partlist[part] . += ST Deletion -= ST continue else - while(partlist[A] > 0) - var/atom/movable/AM = locate(A) in Deletion + while(partlist[part] > 0) + var/atom/movable/AM = locate(part) in Deletion . += AM Deletion -= AM - partlist[A] -= 1 + partlist[part] -= 1 while(Deletion.len) var/DL = Deletion[Deletion.len] Deletion.Cut(Deletion.len) @@ -423,6 +439,7 @@ else result.forceMove(user.drop_location()) to_chat(user, "[TR.name] constructed.") + TR.on_craft_completion(user, result) else to_chat(user, "Construction failed[result]") busy = FALSE @@ -441,26 +458,24 @@ var/list/data = list() data["name"] = R.name data["ref"] = "[REF(R)]" - var/req_text = "" - var/catalyst_text = "" + var/list/req_text = list() + var/list/tool_list = list() + var/list/catalyst_text = list() - for(var/a in R.reqs) + for(var/atom/req_atom as anything in R.reqs) //We just need the name, so cheat-typecast to /atom for speed (even tho Reagents are /datum they DO have a "name" var) //Also these are typepaths so sadly we can't just do "[a]" - var/atom/A = a - req_text += " [R.reqs[A]] [initial(A.name)]," + req_text += "[R.reqs[req_atom]] [initial(req_atom.name)]" + for(var/obj/machinery/content as anything in R.machinery) + req_text += "[R.reqs[content]] [initial(content.name)]" if(R.additional_req_text) req_text += R.additional_req_text - req_text = replacetext(req_text,",","",-1) - data["req_text"] = req_text + data["req_text"] = req_text.Join(", ") - for(var/a in R.chem_catalysts) - var/atom/A = a //cheat-typecast - catalyst_text += " [R.chem_catalysts[A]] [initial(A.name)]," - catalyst_text = replacetext(catalyst_text,",","",-1) - data["catalyst_text"] = catalyst_text + for(var/atom/req_catalyst as anything in R.chem_catalysts) + catalyst_text += "[R.chem_catalysts[req_catalyst]] [initial(req_catalyst.name)]" + data["catalyst_text"] = catalyst_text.Join(", ") - var/list/tool_list = list() for(var/required_quality in R.tool_behaviors) tool_list += required_quality for(var/obj/item/required_path as anything in R.tool_paths) diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm index 6d929c78b42..c9080489d26 100644 --- a/code/datums/components/crafting/recipes.dm +++ b/code/datums/components/crafting/recipes.dm @@ -1,3 +1,7 @@ +///If the machine is used/deleted in the crafting process +#define CRAFTING_MACHINERY_CONSUME 1 +///If the machine is only "used" i.e. it checks to see if it's nearby and allows crafting, but doesn't delete it +#define CRAFTING_MACHINERY_USE 0 /datum/crafting_recipe var/name = "" //in-game display name @@ -10,12 +14,14 @@ var/list/tool_paths var/time = 30 //time in deciseconds var/list/parts = list() //type paths of items that will be placed in the result - var/list/chem_catalysts = list() //like tools but for reagents + var/list/chem_catalysts = list() //like tool_behaviors but for reagents var/category = CAT_NONE //where it shows up in the crafting UI var/subcategory = CAT_NONE var/always_available = TRUE //Set to FALSE if it needs to be learned first. /// Additonal requirements text shown in UI var/additional_req_text + ///Required machines for the craft, set the assigned value of the typepath to CRAFTING_MACHINERY_CONSUME or CRAFTING_MACHINERY_USE. Lazy associative list: type_path key -> flag value. + var/list/machinery /datum/crafting_recipe/New() if(!(result in reqs)) @@ -34,6 +40,9 @@ /datum/crafting_recipe/proc/check_requirements(mob/user, list/collected_requirements) return TRUE +/datum/crafting_recipe/proc/on_craft_completion(mob/user, atom/result) + return + /datum/crafting_recipe/improv_explosive name = "IED" result = /obj/item/grenade/iedcasing @@ -1158,3 +1167,113 @@ /obj/item/aquarium_kit = 1 ) category = CAT_MISC + +/datum/crafting_recipe/alcohol_burner + name = "Alcohol burner" + result = /obj/item/burner + time = 5 SECONDS + reqs = list(/obj/item/reagent_containers/glass/beaker = 1, + /datum/reagent/consumable/ethanol = 15, + /obj/item/paper = 1 + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/oil_burner + name = "Oil burner" + result = /obj/item/burner/oil + time = 5 SECONDS + reqs = list(/obj/item/reagent_containers/glass/beaker = 1, + /datum/reagent/fuel/oil = 15, + /obj/item/paper = 1 + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/fuel_burner + name = "Fuel burner" + result = /obj/item/burner/fuel + time = 5 SECONDS + reqs = list(/obj/item/reagent_containers/glass/beaker = 1, + /datum/reagent/fuel = 15, + /obj/item/paper = 1 + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/thermometer + name = "Thermometer" + tool_behaviors = list(TOOL_WELDER) + result = /obj/item/thermometer + time = 5 SECONDS + reqs = list( + /datum/reagent/mercury = 5, + /obj/item/stack/sheet/glass = 1 + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/thermometer_alt + name = "Thermometer" + result = /obj/item/thermometer/pen + time = 5 SECONDS + reqs = list( + /datum/reagent/mercury = 5, + /obj/item/pen = 1 + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/ph_booklet + name = "pH booklet" + result = /obj/item/ph_booklet + time = 5 SECONDS + reqs = list( + /datum/reagent/universal_indicator = 5, + /obj/item/paper = 1 + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/dropper //Maybe make a glass pipette icon? + name = "Dropper" + result = /obj/item/reagent_containers/dropper + tool_behaviors = list(TOOL_WELDER) + time = 5 SECONDS + reqs = list( + /obj/item/stack/sheet/glass = 1, + ) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/improvised_chem_heater + name = "Improvised chem heater" + result = /obj/machinery/space_heater/improvised_chem_heater + tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_MULTITOOL, TOOL_WIRECUTTER) + time = 15 SECONDS + reqs = list( + /obj/item/stack/cable_coil = 2, + /obj/item/stack/sheet/glass = 2, + /obj/item/stack/sheet/iron = 2, + /datum/reagent/water = 50, + /obj/item/thermometer = 1 + ) + machinery = list(/obj/machinery/space_heater = CRAFTING_MACHINERY_CONSUME) + category = CAT_CHEMISTRY + +/datum/crafting_recipe/improvised_chem_heater/on_craft_completion(mob/user, atom/result) + var/obj/item/stock_parts/cell/cell = locate(/obj/item/stock_parts/cell) in range(1) + if(!cell) + return + var/obj/machinery/space_heater/improvised_chem_heater/heater = result + var/turf/turf = get_turf(cell) + heater.forceMove(turf) + heater.attackby(cell, user) //puts it into the heater + +/datum/crafting_recipe/improvised_coolant + name = "Improvised cooling spray" + tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + result = /obj/item/extinguisher/crafted + time = 10 SECONDS + reqs = list( + /obj/item/toy/crayon/spraycan = 1, + /datum/reagent/water = 20, + /datum/reagent/consumable/ice = 10 + ) + category = CAT_CHEMISTRY + +#undef CRAFTING_MACHINERY_CONSUME +#undef CRAFTING_MACHINERY_USE diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index ab31ca18318..d9606b55282 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -1,6 +1,7 @@ #define HEATER_MODE_STANDBY "standby" #define HEATER_MODE_HEAT "heat" #define HEATER_MODE_COOL "cool" +#define HEATER_MODE_AUTO "auto" /obj/machinery/space_heater anchored = FALSE @@ -16,10 +17,10 @@ circuit = /obj/item/circuitboard/machine/space_heater /// We don't use area power, we always use the cell use_power = NO_POWER_USE - var/obj/item/stock_parts/cell/cell + var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell var/on = FALSE var/mode = HEATER_MODE_STANDBY - var/setMode = "auto" // Anything other than "heat" or "cool" is considered auto. + var/setMode = HEATER_MODE_AUTO // Anything other than "heat" or "cool" is considered auto. var/targetTemperature = T20C var/heatingPower = 20000 var/efficiency = 20000 @@ -32,7 +33,8 @@ /obj/machinery/space_heater/Initialize() . = ..() - cell = new(src) + if(ispath(cell)) + cell = new cell(src) update_appearance() /obj/machinery/space_heater/on_construction() @@ -60,7 +62,8 @@ /obj/machinery/space_heater/update_icon_state() icon_state = "[base_icon_state]-[on ? mode : "off"]" - return ..() + . = ..() + return /obj/machinery/space_heater/update_overlays() . = ..() @@ -179,6 +182,7 @@ data["on"] = on data["mode"] = setMode data["hasPowercell"] = !!cell + data["chemHacked"] = FALSE if(cell) data["powerLevel"] = round(cell.percent(), 1) data["targetTemp"] = round(targetTemperature - T0C, 1) @@ -232,6 +236,174 @@ cell = null . = TRUE +///For use with heating reagents in a ghetto way +/obj/machinery/space_heater/improvised_chem_heater + icon = 'icons/obj/chemical.dmi' + icon_state = "sheater-off" + name = "Improvised chem heater" + desc = "A space heater hacked to reroute heating to a water bath on the top." + panel_open = TRUE //This is always open - since we've injected wires in the panel + //We inherit the cell from the heater prior + cell = null + ///The beaker within the heater + var/obj/item/reagent_containers/beaker = null + ///How powerful the heating is, upgrades with parts. (ala chem_heater.dm's method, basically the same level of heating, but this is restricted) + var/chem_heating_power = 1 + +/obj/machinery/space_heater/improvised_chem_heater/Destroy() + . = ..() + QDEL_NULL(beaker) + +/obj/machinery/space_heater/improvised_chem_heater/process(delta_time) + if(!on) + update_appearance() + return PROCESS_KILL + + if(!is_operational || !cell || cell.charge <= 0) + on = FALSE + update_appearance() + return PROCESS_KILL + + if(!beaker)//No beaker to heat + update_appearance() + return + + if(beaker.reagents.total_volume) + var/power_mod = 0.1 * chem_heating_power + switch(setMode) + if(HEATER_MODE_AUTO) + power_mod *= 0.5 + beaker.reagents.adjust_thermal_energy((targetTemperature - beaker.reagents.chem_temp) * power_mod * delta_time * SPECIFIC_HEAT_DEFAULT * beaker.reagents.total_volume) + beaker.reagents.handle_reactions() + if(HEATER_MODE_HEAT) + if(targetTemperature < beaker.reagents.chem_temp) + return + beaker.reagents.adjust_thermal_energy((targetTemperature - beaker.reagents.chem_temp) * power_mod * delta_time * SPECIFIC_HEAT_DEFAULT * beaker.reagents.total_volume) + if(HEATER_MODE_COOL) + if(targetTemperature > beaker.reagents.chem_temp) + return + beaker.reagents.adjust_thermal_energy((targetTemperature - beaker.reagents.chem_temp) * power_mod * delta_time * SPECIFIC_HEAT_DEFAULT * beaker.reagents.total_volume) + var/requiredEnergy = heatingPower * delta_time * (power_mod * 4) + cell.use(requiredEnergy / efficiency) + beaker.reagents.handle_reactions() + update_appearance() + +/obj/machinery/space_heater/improvised_chem_heater/ui_data() + . = ..() + .["chemHacked"] = TRUE + .["beaker"] = beaker + .["currentTemp"] = beaker ? (round(beaker.reagents.chem_temp - T0C)) : "N/A" + +/obj/machinery/space_heater/improvised_chem_heater/ui_act(action, params) + . = ..() + if(.) + return + switch(action) + if("ejectBeaker") + //Eject doesn't turn it off, so you can preheat for beaker swapping + replace_beaker(usr) + . = TRUE + +///Slightly modified to ignore the open_hatch - it's always open, we hacked it. +/obj/machinery/space_heater/improvised_chem_heater/attackby(obj/item/item, mob/user, params) + add_fingerprint(user) + if(default_unfasten_wrench(user, item)) + return + if(default_deconstruction_crowbar(item)) + return + if(istype(item, /obj/item/stock_parts/cell)) + if(cell) + to_chat(user, "There is already a power cell inside!") + return + else if(!user.transferItemToLoc(item, src)) + return + cell = item + item.add_fingerprint(usr) + + user.visible_message("\The [user] inserts a power cell into \the [src].", "You insert the power cell into \the [src].") + SStgui.update_uis(src) + //reagent containers + if(is_reagent_container(item) && !(item.item_flags & ABSTRACT) && item.is_open_container()) + . = TRUE //no afterattack + var/obj/item/reagent_containers/container = item + if(!user.transferItemToLoc(container, src)) + return + replace_beaker(user, container) + to_chat(user, "You add [container] to [src]'s water bath.") + updateUsrDialog() + return + //Dropper tools + if(beaker) + if(is_type_in_list(item, list(/obj/item/reagent_containers/dropper, /obj/item/ph_meter, /obj/item/ph_paper, /obj/item/reagent_containers/syringe))) + item.afterattack(beaker, user, 1) + return + + +/obj/machinery/space_heater/improvised_chem_heater/on_deconstruction(disassembled = TRUE) + . = ..() + if(disassembled) + beaker?.forceMove(drop_location()) + beaker = null + var/static/bonus_junk = list( + /obj/item/stack/cable_coil = 2, + /obj/item/stack/sheet/glass = 2, + /obj/item/stack/sheet/iron = 2, + /obj/item/thermometer = 1 + ) + for(var/item in bonus_junk) + if(prob(80)) + new item(get_turf(loc)) + +/obj/machinery/space_heater/improvised_chem_heater/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker) + if(!user) + return FALSE + if(beaker) + try_put_in_hand(beaker, user) + beaker = null + if(new_beaker) + beaker = new_beaker + update_appearance() + return TRUE + +/obj/machinery/space_heater/improvised_chem_heater/AltClick(mob/living/user) + . = ..() + if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + replace_beaker(user) + +/obj/machinery/space_heater/improvised_chem_heater/update_icon_state() + . = ..() + if(!on || !beaker || !cell) + icon_state = "sheater-off" + return + if(targetTemperature < beaker.reagents.chem_temp) + icon_state = "sheater-cool" + return + if(targetTemperature > beaker.reagents.chem_temp) + icon_state = "sheater-heat" + return + icon_state = "sheater-off" + +/obj/machinery/space_heater/improvised_chem_heater/RefreshParts() + var/lasers_rating = 0 + var/capacitors_rating = 0 + for(var/obj/item/stock_parts/micro_laser/laser in component_parts) + lasers_rating += laser.rating + for(var/obj/item/stock_parts/capacitor/capacitor in component_parts) + capacitors_rating += capacitor.rating + + heatingPower = lasers_rating * 20000 + + settableTemperatureRange = capacitors_rating * 50 //-20 - 80 at base + efficiency = (capacitors_rating + 1) * 10000 + + targetTemperature = clamp(targetTemperature, + max(settableTemperatureMedian - settableTemperatureRange, TCMB), + settableTemperatureMedian + settableTemperatureRange) + + chem_heating_power = efficiency/20000 //1-2.5 + #undef HEATER_MODE_STANDBY #undef HEATER_MODE_HEAT #undef HEATER_MODE_COOL +#undef HEATER_MODE_AUTO diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm index 3169613c90d..2742b22d911 100644 --- a/code/game/objects/effects/effect_system/effects_water.dm +++ b/code/game/objects/effects/effect_system/effects_water.dm @@ -22,6 +22,8 @@ /obj/effect/particle_effect/water/Bump(atom/A) if(reagents) reagents.expose(A) + if(A.reagents) + A.reagents.expose_temperature(-25) return ..() @@ -51,3 +53,7 @@ /datum/effect_system/steam_spread effect_type = /obj/effect/particle_effect/steam + +/obj/effect/particle_effect/water/Bump(atom/A) + if(A.reagents && reagents) + A.reagents.expose_temperature(reagents.chem_temp) diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index dd9b218e715..ea676a83575 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -44,6 +44,28 @@ sprite_name = "miniFE" dog_fashion = null +/obj/item/extinguisher/crafted + name = "Improvised cooling spray" + desc = "Spraycan turned coolant dipsenser. Can be sprayed on containers to cool them. Refll using water." + icon_state = "coolant0" + inhand_icon_state = "miniFE" + hitsound = null //it is much lighter, after all. + flags_1 = null //doesn't CONDUCT_1 + throwforce = 1 + w_class = WEIGHT_CLASS_SMALL + force = 3 + custom_materials = list(/datum/material/iron = 50, /datum/material/glass = 40) + max_water = 30 + sprite_name = "coolant" + dog_fashion = null + cooling_power = 1.5 + power = 3 + +/obj/item/extinguisher/crafted/attack_self(mob/user) + safety = !safety + icon_state = "[sprite_name][!safety]" + to_chat(user, "[safety ? "You remove the straw and put it on the side of the cool canister" : "You insert the straw, readying it for use"].") + /obj/item/extinguisher/proc/refill() if(!chem) return diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index 0d3bda65f79..b772e877676 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -116,6 +116,20 @@ if(hotness && reagents) reagents.expose_temperature(hotness) to_chat(user, "You heat [name] with [I]!") + + //Cooling method + if(istype(I, /obj/item/extinguisher)) + var/obj/item/extinguisher/extinguisher = I + if(extinguisher.safety) + return + if(extinguisher.reagents.total_volume < 1) + to_chat(user, "\The [extinguisher] is empty!") + return + var/cooling = (0 - reagents.chem_temp) * (extinguisher.cooling_power * 2) + reagents.expose_temperature(cooling) + to_chat(user, "You cool the [name] with the [I]!") + playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3) + extinguisher.reagents.remove_all(1) ..() /obj/item/reagent_containers/food/drinks/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm index eed688d1604..ae28e792e40 100644 --- a/code/modules/food_and_drinks/recipes/food_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm @@ -66,7 +66,7 @@ new /obj/item/food/chocolatebar(location) return -/datum/chemical_reaction/soysauce +/datum/chemical_reaction/food/soysauce results = list(/datum/reagent/consumable/soysauce = 5) required_reagents = list(/datum/reagent/consumable/soymilk = 4, /datum/reagent/toxin/acid = 1) diff --git a/code/modules/reagents/chemistry/items.dm b/code/modules/reagents/chemistry/items.dm index 03a16ced1d7..fc056f524a7 100644 --- a/code/modules/reagents/chemistry/items.dm +++ b/code/modules/reagents/chemistry/items.dm @@ -79,45 +79,15 @@ var/used = FALSE /obj/item/ph_paper/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - var/obj/item/reagent_containers/cont = target - if(!istype(cont)) + if(!is_reagent_container(target)) return + var/obj/item/reagent_containers/cont = target if(used == TRUE) to_chat(user, "[src] has already been used!") return if(!LAZYLEN(cont.reagents.reagent_list)) return - switch(round(cont.reagents.ph, 1)) - if(14 to INFINITY) - color = "#462c83" - if(13 to 14) - color = "#63459b" - if(12 to 13) - color = "#5a51a2" - if(11 to 12) - color = "#3853a4" - if(10 to 11) - color = "#3f93cf" - if(9 to 10) - color = "#0bb9b7" - if(8 to 9) - color = "#23b36e" - if(7 to 8) - color = "#3aa651" - if(6 to 7) - color = "#4cb849" - if(5 to 6) - color = "#b5d335" - if(4 to 5) - color = "#f7ec1e" - if(3 to 4) - color = "#fbc314" - if(2 to 3) - color = "#f26724" - if(1 to 2) - color = "#ef1d26" - if(-INFINITY to 1) - color = "#c6040c" + CONVERT_PH_TO_COLOR(round(cont.reagents.ph, 1), color) desc += " The paper looks to be around a pH of [round(cont.reagents.ph, 1)]" name = "used [name]" used = TRUE @@ -130,7 +100,6 @@ desc = "An electrode attached to a small circuit box that will display details of a solution. Can be toggled to provide a description of each of the reagents. The screen currently displays nothing." icon_state = "pHmeter" icon = 'icons/obj/chemical.dmi' - resistance_flags = FLAMMABLE w_class = WEIGHT_CLASS_TINY ///level of detail for output for the meter var/scanmode = DETAILED_CHEM_OUTPUT @@ -145,7 +114,7 @@ /obj/item/ph_meter/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(!istype(target, /obj/item/reagent_containers)) + if(!is_reagent_container(target)) return var/obj/item/reagent_containers/cont = target if(LAZYLEN(cont.reagents.reagent_list) == null) @@ -162,3 +131,203 @@ out_message += "Analysis: [R.description]\n" to_chat(user, "[out_message.Join()]") desc = "An electrode attached to a small circuit box that will display details of a solution. Can be toggled to provide a description of each of the reagents. The screen currently displays detected vol: [round(cont.volume, 0.01)] detected pH:[round(cont.reagents.ph, 0.1)]." + +/obj/item/burner + name = "Alcohol burner" + desc = "A small table size burner used for heating up beakers." + icon = 'icons/obj/chemical.dmi' + icon_state = "burner" + grind_results = list(/datum/reagent/consumable/ethanol = 5, /datum/reagent/silicon = 10) + item_flags = NOBLUDGEON + resistance_flags = FLAMMABLE + w_class = WEIGHT_CLASS_TINY + heat = 2000 + ///If the flame is lit - i.e. if we're processing and burning + var/lit = FALSE + ///total reagent volume + var/max_volume = 50 + ///What the creation reagent is + var/reagent_type = /datum/reagent/consumable/ethanol + +/obj/item/burner/Initialize() + . = ..() + create_reagents(max_volume, TRANSPARENT)//We have our own refillable - since we want to heat and pour + if(reagent_type) + reagents.add_reagent(reagent_type, 15) + +/obj/item/burner/attackby(obj/item/I, mob/living/user, params) + . = ..() + if(is_reagent_container(I)) + if(lit) + var/obj/item/reagent_containers/container = I + container.reagents.expose_temperature(get_temperature()) + to_chat(user, "You heat up the [I] with the [src].") + playsound(user.loc, 'sound/chemistry/heatdam.ogg', 50, TRUE) + return + else if(I.is_drainable()) //Transfer FROM it TO us. Special code so it only happens when flame is off. + var/obj/item/reagent_containers/container = I + if(!container.reagents.total_volume) + to_chat(user, "[container] is empty and can't be poured!") + return + + if(reagents.holder_full()) + to_chat(user, "[src] is full.") + return + + var/trans = container.reagents.trans_to(src, container.amount_per_transfer_from_this, transfered_by = user) + to_chat(user, "You fill [src] with [trans] unit\s of the contents of [container].") + if(I.heat < 1000) + return + set_lit(TRUE) + user.visible_message("[user] lights up the [src].") + +/obj/item/burner/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(lit) + if(is_reagent_container(target)) + var/obj/item/reagent_containers/container = target + container.reagents.expose_temperature(get_temperature()) + to_chat(user, "You heat up the [src].") + playsound(user.loc, 'sound/chemistry/heatdam.ogg', 50, TRUE) + return + else if(isitem(target)) + var/obj/item/item = target + if(item.heat > 1000) + set_lit(TRUE) + user.visible_message("[user] lights up the [src].") + +/obj/item/burner/update_icon_state() + . = ..() + icon_state = "[initial(icon_state)][lit ? "-on" : ""]" + +/obj/item/burner/proc/set_lit(new_lit) + if(lit == new_lit) + return + lit = new_lit + if(lit) + force = 5 + damtype = BURN + hitsound = 'sound/items/welder.ogg' + attack_verb_continuous = string_list(list("burns", "sings")) + attack_verb_simple = string_list(list("burn", "sing")) + START_PROCESSING(SSobj, src) + else + hitsound = "swing_hit" + force = 0 + attack_verb_continuous = null //human_defense.dm takes care of it + attack_verb_simple = null + STOP_PROCESSING(SSobj, src) + set_light_on(lit) + update_icon() + +/obj/item/burner/extinguish() + set_lit(FALSE) + +/obj/item/burner/attack_self(mob/living/user) + . = ..() + if(.) + return + if(lit) + set_lit(FALSE) + user.visible_message("[user] snuffs out [src]'s flame.") + +/obj/item/burner/attack(mob/living/carbon/M, mob/living/carbon/user) + if(lit && M.IgniteMob()) + message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(M)] on fire with [src] at [AREACOORD(user)]") + log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]") + return ..() + +/obj/item/burner/process() + var/current_heat = 0 + var/number_of_burning_reagents = 0 + for(var/datum/reagent/reagent as anything in reagents.reagent_list) + reagent.burn(reagents) //burn can set temperatures of reagents + if(!isnull(reagent.burning_temperature)) + current_heat += reagent.burning_temperature + number_of_burning_reagents += 1 + reagents.remove_reagent(reagent.type, reagent.burning_volume) + continue + + if(!number_of_burning_reagents) + set_lit(FALSE) + heat = 0 + return + open_flame() + current_heat /= number_of_burning_reagents + heat = current_heat + +/obj/item/burner/get_temperature() + return lit * heat + +/obj/item/burner/oil + name = "Oil burner" + reagent_type = /datum/reagent/fuel/oil + grind_results = list(/datum/reagent/fuel/oil = 5, /datum/reagent/silicon = 10) + +/obj/item/burner/fuel + name = "Fuel burner" + reagent_type = /datum/reagent/fuel + grind_results = list(/datum/reagent/fuel = 5, /datum/reagent/silicon = 10) + +/obj/item/thermometer + name = "thermometer" + desc = "A thermometer for checking a beaker's temperature" + icon_state = "thermometer" + icon = 'icons/obj/chemical.dmi' + item_flags = NOBLUDGEON + w_class = WEIGHT_CLASS_TINY + grind_results = list(/datum/reagent/mercury = 5) + ///The reagents datum that this object is attached to, so we know where we are when it's added to something. + var/datum/reagents/attached_to_reagents + +/obj/item/thermometer/Destroy() + QDEL_NULL(attached_to_reagents) //I have no idea how you can destroy this, but not the beaker, but here we go + return ..() + +/obj/item/thermometer/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(target.reagents) + if(!user.transferItemToLoc(src, target)) + return + attached_to_reagents = target.reagents + to_chat(user, "You add the [src] to the [target].") + ui_interact(usr, null) + +/obj/item/thermometer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Thermometer", name) + ui.open() + +/obj/item/thermometer/ui_close(mob/user) + . = ..() + remove_thermometer(user) + +/obj/item/thermometer/ui_status(mob/user) + if(!(in_range(src, user))) + return UI_CLOSE + return UI_INTERACTIVE + +/obj/item/thermometer/ui_state(mob/user) + return GLOB.physical_state + +/obj/item/thermometer/ui_data(mob/user) + if(!attached_to_reagents) + ui_close(user) + var/data = list() + data["Temperature"] = round(attached_to_reagents.chem_temp) + return data + +/obj/item/thermometer/proc/remove_thermometer(mob/target) + try_put_in_hand(src, target) + attached_to_reagents = null + +/obj/item/thermometer/proc/try_put_in_hand(obj/object, mob/living/user) + to_chat(user, "You remove the [src] from the [attached_to_reagents.my_atom].") + if(!issilicon(user) && in_range(src.loc, user)) + user.put_in_hands(object) + else + object.forceMove(drop_location()) + +/obj/item/thermometer/pen + color = "#888888" diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index a2986c8042b..542c8045168 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -81,10 +81,15 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) var/impure_chem = /datum/reagent/impurity /// If the impurity is below 0.5, replace ALL of the chem with inverse_chem upon metabolising var/inverse_chem_val = 0.25 - /// What chem is metabolised when purity is below inverse_chem_val + /// What chem is metabolised when purity is below inverse_chem_val var/inverse_chem = /datum/reagent/impurity/toxic ///what chem is made at the end of a reaction IF the purity is below the recipies purity_min at the END of a reaction only var/failed_chem = /datum/reagent/consumable/failed_reaction + ///Thermodynamic vars + ///How hot this reagent burns when it's on fire - null means it can't burn + var/burning_temperature = null + ///How much is consumed when it is burnt per second + var/burning_volume = 0.5 ///Assoc list with key type of addiction this reagent feeds, and value amount of addiction points added per unit of reagent metabolzied (which means * REAGENTS_METABOLISM every life()) var/list/addiction_types = null @@ -129,6 +134,10 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) return SEND_SIGNAL(src, COMSIG_REAGENT_EXPOSE_TURF, exposed_turf, reac_volume) +///Called whenever a reagent is on fire, or is in a holder that is on fire. (WIP) +/datum/reagent/proc/burn(datum/reagents/holder) + return + /// Called from [/datum/reagents/proc/metabolize] /datum/reagent/proc/on_mob_life(mob/living/carbon/M, delta_time, times_fired) current_cycle++ diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 667a185eb61..a7d0367b6f2 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -14,6 +14,8 @@ taste_description = "alcohol" metabolization_rate = 0.5 * REAGENTS_METABOLISM ph = 7.33 + burning_temperature = 2193//ethanol burns at 1970C (at it's peak) + burning_volume = 0.1 var/boozepwr = 65 //Higher numbers equal higher hardness, higher hardness equals more intense alcohol poisoning /* diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 078b7778ea9..e52ac513f92 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -156,6 +156,8 @@ color = "#0000C8" taste_description = "blue" ph = 11 + burning_temperature = 20 //cold burning + burning_volume = 0.1 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/carbon/M, delta_time, times_fired) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 61e618b8dc8..06311afbfe2 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -12,6 +12,12 @@ penetrates_skin = NONE ph = 7.4 + // FEED ME +/datum/reagent/blood/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) + . = ..() + if(chems.has_reagent(type, 1)) + mytray.adjustPests(rand(2,3)) + /datum/reagent/blood/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=0) . = ..() if(data && data["viruses"]) @@ -224,6 +230,14 @@ ph = 7.5 //God is alkaline chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + // Holy water. Mostly the same as water, it also heals the plant a little with the power of the spirits. Also ALSO increases instability. +/datum/reagent/water/holywater/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) + if(chems.has_reagent(type, 1)) + mytray.adjustWater(round(chems.get_reagent_amount(type) * 1)) + mytray.adjustHealth(round(chems.get_reagent_amount(type) * 0.1)) + if(myseed) + myseed.adjust_instability(round(chems.get_reagent_amount(type) * 0.15)) + /datum/reagent/water/holywater/on_mob_metabolize(mob/living/L) ..() ADD_TRAIT(L, TRAIT_HOLY, type) @@ -1103,6 +1117,8 @@ glass_desc = "Unless you're an industrial tool, this is probably not safe for consumption." penetrates_skin = NONE ph = 4 + burning_temperature = 1725 //more refined than oil + burning_volume = 0.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED addiction_types = list(/datum/addiction/alcohol = 4) @@ -1711,6 +1727,8 @@ reagent_state = LIQUID color = "#2D2D2D" taste_description = "oil" + burning_temperature = 1200//Oil is crude + burning_volume = 0.05 //but has a lot of hydrocarbons chemical_flags = REAGENT_CAN_BE_SYNTHESIZED addiction_types = null @@ -2573,3 +2591,16 @@ M.adjustBruteLoss(2 * REM * delta_time, FALSE) ..() +/datum/reagent/universal_indicator + name = "Universal indicator" + description = "A solution that can be used to create pH paper booklets, or sprayed on things to colour them by their pH." + taste_description = "a strong chemical taste" + color = "#1f8016" + +//Colours things by their pH +/datum/reagent/universal_indicator/expose_atom(atom/exposed_atom, reac_volume) + . = ..() + if(exposed_atom.reagents) + var/color + CONVERT_PH_TO_COLOR(exposed_atom.reagents.ph, color) + exposed_atom.add_atom_colour(color, WASHABLE_COLOUR_PRIORITY) diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index 6ad017e4988..00090822dbb 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -216,8 +216,14 @@ metabolization_rate = 0.5 * REAGENTS_METABOLISM taste_description = "bitterness" self_consuming = TRUE + burning_volume = 0.05 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/cryostylane/burn(datum/reagents/holder) + if(holder.has_reagent(/datum/reagent/oxygen)) + burning_temperature = 0//king chilly + return + burning_temperature = null /datum/reagent/cryostylane/on_mob_life(mob/living/carbon/M, delta_time, times_fired) //TODO: code freezing into an ice cube if(M.reagents.has_reagent(/datum/reagent/oxygen)) @@ -242,6 +248,8 @@ metabolization_rate = 0.5 * REAGENTS_METABOLISM taste_description = "bitterness" self_consuming = TRUE + burning_temperature = null + burning_volume = 0.05 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/pyrosium/on_mob_life(mob/living/carbon/M, delta_time, times_fired) @@ -253,6 +261,12 @@ humi.adjust_coretemperature(15 * REM * delta_time) ..() +/datum/reagent/pyrosium/burn(datum/reagents/holder) + if(holder.has_reagent(/datum/reagent/oxygen)) + burning_temperature = 3500 + return + burning_temperature = null + /datum/reagent/teslium //Teslium. Causes periodic shocks, and makes shocks against the target much more effective. name = "Teslium" description = "An unstable, electrically-charged metallic slurry. Periodically electrocutes its victim, and makes electrocutions against them more deadly. Excessively heating teslium results in dangerous destabilization. Do not allow to come into contact with water." diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 20a6b9bc7f6..7c2dae37565 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -78,6 +78,8 @@ material = /datum/material/plasma penetrates_skin = NONE ph = 4 + burning_temperature = 4500//plasma is hot!! + burning_volume = 0.3//But burns fast chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/toxin/plasma/on_new(data) diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index 13fe4af901d..7cef6219221 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -21,7 +21,6 @@ ///Determines if a chemical reaction can occur inside a mob var/mob_react = TRUE - ///The message shown to nearby people upon mixing, if applicable var/mix_message = "The solution begins to bubble." ///The sound played upon mixing, if applicable diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index be1bcdca8be..e879e7a12a9 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -643,3 +643,50 @@ for(var/i in 1 to created_volume) new /obj/item/stack/sheet/mineral/silver(location) +//////////////////////////////////// Water //////////////////////////////////////////////// + +/datum/chemical_reaction/ice + results = list(/datum/reagent/consumable/ice = 1.09)//density + required_reagents = list(/datum/reagent/water = 1) + is_cold_recipe = TRUE + required_temp = 274 // So we can be sure that basic ghetto rigged stuff can freeze + optimal_temp = 200 + overheat_temp = 0 + optimal_ph_min = 0 + optimal_ph_max = 14 + thermic_constant = 0 + H_ion_release = 0 + rate_up_lim = 50 + purity_min = 0 + mix_message = "The solution freezes up into ice!" + reaction_flags = REACTION_COMPETITIVE + +/datum/chemical_reaction/water + results = list(/datum/reagent/water = 0.92)//rough density excahnge + required_reagents = list(/datum/reagent/consumable/ice = 1) + required_temp = 275 + optimal_temp = 350 + overheat_temp = 99999 + optimal_ph_min = 0 + optimal_ph_max = 14 + thermic_constant = 0 + H_ion_release = 0 + rate_up_lim = 50 + purity_min = 0 + mix_message = "The ice melts back into water!" + +//////////////////////////////////// + +/datum/chemical_reaction/universal_indicator + results = list(/datum/reagent/universal_indicator = 3)//rough density excahnge + required_reagents = list(/datum/reagent/ash = 1, /datum/reagent/consumable/ethanol = 1, /datum/reagent/iodine = 1) + required_temp = 274 + optimal_temp = 350 + overheat_temp = 99999 + optimal_ph_min = 0 + optimal_ph_max = 14 + thermic_constant = 0 + H_ion_release = 0 + rate_up_lim = 50 + purity_min = 0 + mix_message = "The mixture's colors swirl together." diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 547e169fe22..011bd9c73d2 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -32,9 +32,6 @@ RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change) RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del) -/obj/item/reagent_containers/Destroy() - return ..() - /obj/item/reagent_containers/attack(mob/living/M, mob/living/user, params) if (!user.combat_mode) return diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index eb51a616cb1..f28719a911a 100755 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -86,6 +86,20 @@ reagents.expose_temperature(hotness) to_chat(user, "You heat [name] with [I]!") + //Cooling method + if(istype(I, /obj/item/extinguisher)) + var/obj/item/extinguisher/extinguisher = I + if(extinguisher.safety) + return + if (extinguisher.reagents.total_volume < 1) + to_chat(user, "\The [extinguisher] is empty!") + return + var/cooling = (0 - reagents.chem_temp) * extinguisher.cooling_power * 2 + reagents.expose_temperature(cooling) + to_chat(user, "You cool the [name] with the [I]!") + playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3) + extinguisher.reagents.remove_all(1) + if(istype(I, /obj/item/food/egg)) //breaking eggs var/obj/item/food/egg/E = I if(reagents) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 9f6549a0fec..05f4baea219 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -129,6 +129,21 @@ if(hotness && reagents) reagents.expose_temperature(hotness) to_chat(user, "You heat [name] with [I]!") + + //Cooling method + if(istype(I, /obj/item/extinguisher)) + var/obj/item/extinguisher/extinguisher = I + if(extinguisher.safety) + return + if (extinguisher.reagents.total_volume < 1) + to_chat(user, "\The [extinguisher] is empty!") + return + var/cooling = (0 - reagents.chem_temp) * extinguisher.cooling_power * 2 + reagents.expose_temperature(cooling) + to_chat(user, "You cool the [name] with the [I]!") + playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3) + extinguisher.reagents.remove_all(1) + return ..() /obj/item/reagent_containers/spray/verb/empty() diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index 41ca84d9f45..5260ae293e5 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi index 44fbde67218..40354b63abc 100644 Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.js index 92c1f80e6c0..8a094bb4c6b 100644 --- a/tgui/packages/tgui/constants.js +++ b/tgui/packages/tgui/constants.js @@ -30,6 +30,11 @@ export const COLORS = { burn: '#e67e22', brute: '#e74c3c', }, + // reagent / chemistry related colours + reagent: { + acidicbuffer: "#fbc314", + basicbuffer: "#3853a4", + }, }; // Colors defined in CSS diff --git a/tgui/packages/tgui/interfaces/ChemHeater.js b/tgui/packages/tgui/interfaces/ChemHeater.js index b70e6ba3eac..2686b0dce62 100644 --- a/tgui/packages/tgui/interfaces/ChemHeater.js +++ b/tgui/packages/tgui/interfaces/ChemHeater.js @@ -1,12 +1,11 @@ -import { resolveAsset } from '../assets'; import { round, toFixed } from 'common/math'; +import { resolveAsset } from '../assets'; import { useBackend } from '../backend'; -import { AnimatedNumber, Box, Button, NumberInput, Section, ProgressBar, Table, RoundGauge, Flex, Icon, TextArea } from '../components'; -import { TableCell, TableRow } from '../components/Table'; +import { AnimatedNumber, Box, Button, Flex, Icon, NumberInput, ProgressBar, RoundGauge, Section, Table } from '../components'; +import { COLORS } from '../constants'; import { Window } from '../layouts'; import { BeakerContents } from './common/BeakerContents'; - export const ChemHeater = (props, context) => { const { act, data } = useBackend(context); const { @@ -26,7 +25,7 @@ export const ChemHeater = (props, context) => { beakerContents = [], activeReactions = [], } = data; - return ( + return ( @@ -51,17 +50,15 @@ export const ChemHeater = (props, context) => { )}> - + Heat - + Buffers - - + + { onDrag={(e, value) => act('disp_vol', { target: value, })} /> - + - - + + Target: - - + + { onDrag={(e, value) => act('temperature', { target: value, })} /> - - + + Acidic: - - + +
{!!isBeakerLoaded && ( @@ -175,7 +172,7 @@ export const ChemHeater = (props, context) => { {(_, value) => ( - { No active reactions. ) || ( - - - +
+ + Reaction - - + + {upgradeLevel < 4 ? "Status" : "Reaction quality"} - - + + Target - - + + {activeReactions.map(reaction => ( - - + + {reaction.name} - - + + {upgradeLevel < 4 && ( - ) || ( {(_, value) => ( - { )} )} - - + + {upgradeLevel > 2 && ( {reaction.targetVol}u ) || ( {reaction.targetVol}u )} - - + + ))} - +
)} )} {tutorialMessage && ( -
- - {tutorialMessage} + + {tutorialMessage}
)}
{ title="Power" buttons={( <> + {!!data.chemHacked && ( +