diff --git a/apollo.dme b/apollo.dme index 05d2df967e..f3fddd5450 100644 --- a/apollo.dme +++ b/apollo.dme @@ -34,6 +34,14 @@ #include "code\__HELPERS\type2type.dm" #include "code\__HELPERS\unsorted.dm" #include "code\_helpers\matrices.dm" +#include "code\_helpers\LightningLib\Bar.dm" +#include "code\_helpers\LightningLib\Beam.dm" +#include "code\_helpers\LightningLib\Bolt.dm" +#include "code\_helpers\LightningLib\BranchedBolt.dm" +#include "code\_helpers\LightningLib\Line.dm" +#include "code\_helpers\LightningLib\math.dm" +#include "code\_helpers\LightningLib\segment.dm" +#include "code\_helpers\LightningLib\Vector.dm" #include "code\_onclick\adjacent.dm" #include "code\_onclick\ai.dm" #include "code\_onclick\click.dm" @@ -104,6 +112,7 @@ #include "code\controllers\Processes\emergencyShuttle.dm" #include "code\controllers\Processes\event.dm" #include "code\controllers\Processes\factions.dm" +#include "code\controllers\Processes\fusion.dm" #include "code\controllers\Processes\garbage.dm" #include "code\controllers\Processes\hanger.dm" #include "code\controllers\Processes\hanger_controller.dm" @@ -199,6 +208,7 @@ #include "code\datums\wires\airlock.dm" #include "code\datums\wires\alarm.dm" #include "code\datums\wires\apc.dm" +#include "code\datums\wires\arc_emitter_wires.dm" #include "code\datums\wires\autolathe.dm" #include "code\datums\wires\breakerbox.dm" #include "code\datums\wires\bubble_shield_gen_wires.dm" @@ -1496,6 +1506,16 @@ #include "code\modules\power\antimatter\containment_jar.dm" #include "code\modules\power\antimatter\control.dm" #include "code\modules\power\antimatter\shielding.dm" +#include "code\modules\power\fusion\arc_emitter.dm" +#include "code\modules\power\fusion\base.dm" +#include "code\modules\power\fusion\computer.dm" +#include "code\modules\power\fusion\controller.dm" +#include "code\modules\power\fusion\core.dm" +#include "code\modules\power\fusion\fabricator.dm" +#include "code\modules\power\fusion\fusion_ball.dm" +#include "code\modules\power\fusion\plasma.dm" +#include "code\modules\power\fusion\ring.dm" +#include "code\modules\power\fusion\upgrades.dm" #include "code\modules\power\rust\areas.dm" #include "code\modules\power\rust\circuits_and_design.dm" #include "code\modules\power\rust\core_control.dm" diff --git a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm index bec10086df..0db5680b20 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm @@ -1,5 +1,3 @@ -#define ADIABATIC_EXPONENT 0.667 //Actually adiabatic exponent - 1. - /obj/machinery/atmospherics/pipeturbine name = "turbine" desc = "A gas turbine. Converting pressure into energy since 1884." diff --git a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm index 6d5f3d167f..125614c28a 100644 --- a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm +++ b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm @@ -8,6 +8,7 @@ desc = "Exchanges heat between two input gases. Setup for fast heat transfer" var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null + var/obj/machinery/power/fusion/plasma/plasma = null var/update_cycle update_icon() @@ -27,41 +28,48 @@ partner = target partner.partner = src break - ..() process() ..() - if(!partner) + var/datum/gas_mixture/partner_air = null + if(!partner && !plasma) return 0 + else if(!isnull(partner)) + partner_air = partner.air_contents + else if (plasma.transfering) + partner_air = plasma.air_contents +/* //I dont think the following is needed as non of the other gas heating/cooling machines use it. if(!air_master || air_master.current_cycle <= update_cycle) return 0 update_cycle = air_master.current_cycle - partner.update_cycle = air_master.current_cycle - + if(!isnull(partner)) + partner.update_cycle = air_master.current_cycle +*/ var/air_heat_capacity = air_contents.heat_capacity() - var/other_air_heat_capacity = partner.air_contents.heat_capacity() + var/other_air_heat_capacity = partner_air.heat_capacity() var/combined_heat_capacity = other_air_heat_capacity + air_heat_capacity var/old_temperature = air_contents.temperature - var/other_old_temperature = partner.air_contents.temperature + var/other_old_temperature = partner_air.temperature if(combined_heat_capacity > 0) - var/combined_energy = partner.air_contents.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature + var/combined_energy = partner_air.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature var/new_temperature = combined_energy/combined_heat_capacity air_contents.temperature = new_temperature - partner.air_contents.temperature = new_temperature + partner_air.temperature = new_temperature if(network) if(abs(old_temperature-air_contents.temperature) > 1) network.update = 1 - if(partner.network) - if(abs(other_old_temperature-partner.air_contents.temperature) > 1) - partner.network.update = 1 + if(isnull(plasma))//Plasma had not variable called network this will cause erros otherwise + if(partner.network) + if(abs(other_old_temperature-partner_air.temperature) > 1) + partner.network.update = 1 return 1 diff --git a/code/ZAS/Gas.dm b/code/ZAS/Gas.dm index 898d56723d..883d8f0ff9 100644 --- a/code/ZAS/Gas.dm +++ b/code/ZAS/Gas.dm @@ -12,6 +12,13 @@ specific_heat = 20 // J/(mol*K) molar_mass = 0.028 // kg/mol +/xgm_gas/hydrogen + id = "hydrogen" + name = "Hydrogen" + specific_heat = 28 // J/(mol*K) + molar_mass = 0.020 // kg/mol + flags = XGM_GAS_FUEL + /xgm_gas/carbon_dioxide id = "carbon_dioxide" name = "Carbon Dioxide" diff --git a/code/ZAS/_gas_mixture_xgm.dm b/code/ZAS/_gas_mixture_xgm.dm index c7fd0b6910..719cd79a53 100644 --- a/code/ZAS/_gas_mixture_xgm.dm +++ b/code/ZAS/_gas_mixture_xgm.dm @@ -139,6 +139,9 @@ /datum/gas_mixture/proc/get_thermal_energy_change(var/new_temperature) return heat_capacity()*(max(new_temperature, 0) - temperature) +//Returns the thermal energy stored in the gas, usefull for deviding energy between gas systems. +/datum/gas_mixture/proc/get_thermal_energy() + return heat_capacity()*temperature //Technically vacuum doesn't have a specific entropy. Just use a really big number (infinity would be ideal) here so that it's easy to add gas to vacuum and hard to take gas out. #define SPECIFIC_ENTROPY_VACUUM 150000 @@ -172,6 +175,12 @@ //group_multiplier gets divided out in volume/gas[gasid] - also, V/(m*T) = R/(partial pressure) var/molar_mass = gas_data.molar_mass[gasid] var/specific_heat = gas_data.specific_heat[gasid] +/* if(gasid == "hydrogen") //Hydrogen gass debug + world << "Molar mas of [gasid] = [molar_mass]" + world << "Specific heat of [gasid] = [specific_heat]" + world << "Temperature of [gasid] = [temperature]" + world << "gas\[gasid\] returns [gas[gasid]]" +*/ return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*volume/(gas[gasid] * temperature)) * (molar_mass*specific_heat*temperature)**(2/3) + 1 ) + 15 ) //alternative, simpler equation diff --git a/code/__HELPERS/datum_pool.dm b/code/__HELPERS/datum_pool.dm index 84979532ad..da8835ac46 100644 --- a/code/__HELPERS/datum_pool.dm +++ b/code/__HELPERS/datum_pool.dm @@ -1,4 +1,3 @@ - /* /tg/station13 /atom/movable Pool: --------------------------------- diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 5574ae818d..68e1914d78 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -16,6 +16,14 @@ var/list/sqrtTable = list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, var/a = arccos(x / sqrt(x*x + y*y)) return y >= 0 ? a : -a +/proc/alt_atan2(x,y) + return (x||y)&&(y>=0 ? arccos(x/sqrt(x*x+y*y)) : 360-arccos(x/sqrt(x*x+y*y))) + +/proc/alt_Rand(min, max, precision = 3) + var/d = 10 ** precision + + return rand(min * d, max * d) / d + /proc/Ceiling(x) return -round(-x) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 20d2ba1380..00f2f77ef7 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -85,7 +85,7 @@ return 1 -/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. +/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. //Give agle in the x direction not from current dir. if(!start || !end) return 0 var/dy var/dx diff --git a/code/_helpers/LightningLib/Bar.dm b/code/_helpers/LightningLib/Bar.dm new file mode 100644 index 0000000000..d5e58256a8 --- /dev/null +++ b/code/_helpers/LightningLib/Bar.dm @@ -0,0 +1,48 @@ +/** + * adjustable bar + */ + +beam/bar + + var/list/objects + var/index + + + /** + * Draws a adjustable bar of type with a color between two assigned vectors on z or client screen + * + * @param z the map z level to draw on, if z is client it will draw on client screen + * @param type basic segment to use when drawing + * @param color color of the segment + * @param thickness thickness of the segment + */ + Draw(z, type = /obj/segment, color = "#fff", thickness = 1) + + objects = list() + for(var/line/segment in segments) + var/obj/o = segment.Draw(z, type, color, thickness) + objects += o + + index = objects.len + + proc + /** + * Adjusts the bar to a percent + * + * @param percent percent of bar filled + */ + Adjust(percent) + set waitfor = 0 + + var/newIndex = round((percent * objects.len) / 100) + var/s = newIndex > index ? 1 : -1 + + for(var/i = index to newIndex step s) + var/obj/o = objects[i] + + o.invisibility = !o.invisibility + + sleep(world.tick_lag) + + index = newIndex + diff --git a/code/_helpers/LightningLib/Beam.dm b/code/_helpers/LightningLib/Beam.dm new file mode 100644 index 0000000000..ae69be0fc1 --- /dev/null +++ b/code/_helpers/LightningLib/Beam.dm @@ -0,0 +1,110 @@ +/** + * a line made of line segments, used to create a growing animated line (useful for beams, bars etc) + */ + +beam + var + list/segments + fade + + /** + * Constructs the beam, from vector source to vector dest + * + * @param source source vector, where the bolt starts + * @param dest destination vector, where the beam ends + * @param placement distance between every segment, lower means more segments, default of 32 + * @param fade assigns fade out rate, default of 25 + */ + New(vector/source, vector/dest, placement = 32, fade = 25) + ..() + + segments = createBeam(source, dest, placement) + src.fade = 25 + + proc + /** + * Draws a beam of type with a color between two assigned vectors on z or client screen + * + * @param z the map z level to draw on, if z is client it will draw on client screen + * @param type basic segment to use when drawing + * @param color color of the beam + * @param thickness thickness of the beam + */ + Draw(z, type = /obj/segment, color = "#fff", thickness = 1) + set waitfor = 0 + var/pos = 1 + for(var/line/segment in segments) + var/obj/o = segment.Draw(z, type, color, thickness) + Effect(o, pos++) + sleep(world.tick_lag) + + /** + * Applys animation to beam segment + * this could be overriden by child types to allow different animations to beam + * by default, a beam will fully grow then begin to fade out + * + * @param o the object segment, each beam is made of several segments + * @param pos the position of the segment, this could be used to calculate the delay at which it is displayed to provide more control over animations + */ + Effect(obj/o, pos) + set waitfor = 0 + sleep(world.tick_lag * (segments.len - pos)) + + animate(o, alpha = 0, time = 255 / fade, loop = 1) + + sleep(255 / fade) + Dispose(o, pos) + + /** + * Handles soft deletion of beam segments + * by default after a beam faded it will be disposed + * + * @param o the object segment to dispose + */ + Dispose(obj/o) + o.loc = null + + /** + * Returns a list of segments from vector source to vector dest + * + * @param source source vector, where the beam starts + * @param dest destination vector, where the beam ends + * @return dest a list of line segments forming a beam + */ + createBeam(vector/source, vector/dest, placement = 32) + var/list/results = list() + + var/vector/tangent = vectorSubtract(dest, source) + var/length = tangent.Length() + + var/vector/prevPoint = source + for(var/i = 1 to length / placement) + + var/pos = i / (length / placement) + var/vector/endPoint = new (source.X + (tangent.X * pos), source.Y + (tangent.Y * pos)) + + endPoint.Round() + var/line/l = new(prevPoint, endPoint) + results += l + + prevPoint = endPoint + + var/line/l = new(prevPoint, dest) + results += l + + return results + + /** + * Returns a list of turfs between the beam's starting vector to the beam's end vector + * It can return null if no turfs are found. + * + * @param z the map z level to search + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum + * @return a list of turfs the beam passes on + */ + GetTurfs(z, accurate = 16) + var/line/start = segments[1] + var/line/end = segments[segments.len] + + return vectorGetTurfs(start.A, end.B, z, accurate) \ No newline at end of file diff --git a/code/_helpers/LightningLib/Bolt.dm b/code/_helpers/LightningLib/Bolt.dm new file mode 100644 index 0000000000..b2b4dab216 --- /dev/null +++ b/code/_helpers/LightningLib/Bolt.dm @@ -0,0 +1,207 @@ +/** + * A lightning bolt drawn between two vectors + */ + +bolt + var + list/segments + fade + + tmp + obj/lastCreatedBolt + + /** + * Constructs the bolt, from vector source to vector dest + * + * @param source source vector, where the bolt starts + * @param dest destination vector, where the bolt ends + * @param fade assigns fade out rate, default of 50 + */ + New(vector/source, vector/dest, fade = 50) + ..() + + segments = createBolt(source, dest) + src.fade = 50 + + proc + /** + * Draws a bolt of type with a color between two assigned vectors on z or client screen + * + * @param z the map z level to draw on, if z is client it will draw on client screen + * @param type basic segment to use when drawing + * @param color color of the bolt + * @param thickness thickness of the bolt + * @param split if set to 1 will create an obj for each segment, if not it will only create one object with segments as overlays + */ + Draw(z, type = /obj/segment, color = "#fff", thickness = 1, split = 0) + + if(split) + for(var/line/segment in segments) + var/obj/o = segment.Draw(z, type, color, thickness) + Effect(o) + else + var/line/l = segments[1] + var/obj/o = new (l.A.Locate(z)) + + lastCreatedBolt = o + + o.alpha = 255 + o.color = color + + var/list/lines = list() + for(var/line/segment in segments) + lines += segment.Draw(o, type, color, thickness) + + o.overlays = lines + + Effect(o) + + /** + * Applys animation to lightning bolt segment + * this could be overriden by child types to allow different animations to the lightning bolt + * by default, a lightning bolt will fade out then be disposed + * + * @param o the object segment, each lightning bolt is made of several segments + */ + Effect(obj/o) + set waitfor = 0 + animate(o, alpha = 0, time = 255 / fade, loop = 1) + + sleep(255 / fade) + Dispose(o) + + /** + * Rotates last created bolt to a different angle + * + * @param angle the new angle of the bolt + */ + Rotate(angle) + + var/line/firstLine = segments[1] + var/line/lastLine = segments[segments.len] + + var/vector/tangent = vectorSubtract(lastLine.B, firstLine.A) + var/rotation = __atan2(tangent.Y, tangent.X) - 90 + + angle -= rotation + + lastCreatedBolt.transform = turn(matrix(), angle) + + /** + * Handles soft deletion of lightning bolt segments + * by default after a lightning bolt faded it will be disposed + * + * @param o the object segment to dispose + */ + Dispose(obj/o) + lastCreatedBolt = null + o.loc = null + + /** + * Returns a list of segments from vector source to vector dest + * + * @param source source vector, where the bolt starts + * @param dest destination vector, where the bolt ends + * @return dest a list of line segments forming a lightning bolt + */ + createBolt(vector/source, vector/dest) + var/list/results = list() + + var/vector/tangent = vectorSubtract(dest, source) + var/vector/normal = vectorNormalize(new (tangent.Y, -tangent.X)) + + var/length = tangent.Length() + + var/list/positions = list(0) + + var/growth = 1 / (length / 4) + var/p = 0 + for(var/i = 1 to length / 4) + var/r = __rand(growth / 3, growth * 3) + p += r + positions += p + + if(p >= 1) break + + var/const/Sway = 80 + var/const/Jaggedness = 1 / Sway + + var/vector/prevPoint = source + var/prevDisplacement = 0 + for(var/i = 2 to positions.len) + var/pos = positions[i] + + // used to prevent sharp angles by ensuring very close positions also have small perpendicular variation. + var/scale = ((length) * Jaggedness) * (pos - positions[i - 1]) + + // defines an envelope. Points near the middle of the bolt can be further from the central line. + var/envelope = pos > 0.95 ? 20 * (1 - pos) : 1 + + var/displacement = __rand(-Sway, Sway) + + displacement -= (displacement - prevDisplacement) * (1 - scale) + displacement *= envelope + + + var/vector/point = new (source.X + (tangent.X * pos) + (normal.X * displacement), source.Y + (tangent.Y * pos) + (normal.Y * displacement)) + point.Round() + + var/line/l = new(prevPoint, point) + results += l + + prevPoint = point + prevDisplacement = displacement + + var/line/l = new(prevPoint, dest) + results += l + + return results + + /** + * Returns a vector at a given fraction 0 to 1, 0 being the start of the bolt and 1 being the end + * + * @param fraction 0 to 1, 0 being the start of the bolt and 1 being the end + * @return a vector at fraction point of the bolt + */ + GetPoint(var/fraction) + var/index = round(fraction * segments.len) + + index = min(index, segments.len) + index = max(index, 1) + + var/line/l = segments[index] + + return l.A + + /** + * Returns a list of turfs between the bolt's starting vector to the bolt's end vector + * because this only checks first and last vectors it returns a form of line between both vectors and can be inaccurate if bolt segments stray too far + * It can return null if no turfs are found. + * + * @param z the map z level to search + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum, it should be noted even at 1 this will not be too accurate, use GetAllTurfs() for a more accurate result + * @return a partial list of turfs the bolt passes on + */ + GetTurfs(z, accurate = 16) + var/line/start = segments[1] + var/line/end = segments[segments.len] + + return vectorGetTurfs(start.A, end.B, z, accurate) + + /** + * Returns a list of turfs between the bolt's starting vector to the bolt's end vector checking all segments + * It can return null if no turfs are found. + * + * @param z the map z level to search + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum + * @return a list of turfs the bolt passes on + */ + GetAllTurfs(z, accurate = 16) + var/list/locs = list() + + for(var/line/segment in segments) + locs = locs|segment.GetTurfs(z, accurate) + + return locs.len ? locs : null \ No newline at end of file diff --git a/code/_helpers/LightningLib/BranchedBolt.dm b/code/_helpers/LightningLib/BranchedBolt.dm new file mode 100644 index 0000000000..e5a13eca41 --- /dev/null +++ b/code/_helpers/LightningLib/BranchedBolt.dm @@ -0,0 +1,127 @@ +/** + * A lightning bolt drawn between two vectors with 3 to 6 branched lightning bolts + */ + +BranchedBolt + + var + list/bolts + vector/end + + fade + + /** + * Constructs the branched bolt, from vector source to vector dest + * + * @param source source vector, where the bolt starts + * @param dest destination vector, where the bold ends + * @param fade assigns fade out rate, default of 50 + */ + New(vector/start, vector/end, fade = 50, targets=null) + ..() + + src.end = end + src.fade = fade + + Create(start, end, targets) + + proc + /** + * Draws a branched lightning bolt of type with a color between two assigned vectors on z or client screen + * + * @param z the map z level to draw on, if z is client it will draw on client screen + * @param type basic segment to use when drawing + * @param color color of the branched bolt + * @param thickness thickness of the branched bolt + * @param split if set to 1 will create an obj for each segment, if not it will only create one object with segments as overlays + */ + Draw(z, type = /obj/segment, color = "#fff", thickness = 1, split = 0) + for(var/bolt/b in bolts) + b.fade = fade + b.Draw(z, type, color, thickness, split) + + /** + * Initializes the branched lightning bolts list + * the first bolt in the list will be the main bolt between the two given vectors with the addition of 3 to 6 branched bolts + * + * @param source source vector, where the bolt starts + * @param dest destination vector, where the bolt ends + */ + Create(vector/start, vector/end, targets = null) + var/bolt/mainbolt = new (start, end) + + bolts = list(mainbolt) + + var/branches + var/boltTargets = FALSE + + if(!targets) + branches = rand(3, 6) + else if(isnum(targets)) + branches = targets + else + branches = length(targets) + boltTargets = TRUE + + var/list/positions = list() + + var/growth = 0.5 / branches + var/p = 0 + for(var/i = 1 to branches) + var/r = __rand(growth / 3, growth * 3) + p += r + positions += p + + if(p >= 0.50) break + + var/vector/diff = vectorSubtract(end, start) + + for(var/i = 1 to positions.len) + // bolt.GetPoint() gets the position of the lightning bolt at specified fraction (0 = start of bolt, 1 = end) + var/vector/boltStart = mainbolt.GetPoint(positions[i]) + + var/vector/boltEnd + if(boltTargets) + var/atom/target = targets[i] + boltEnd = new (target.x * world.icon_size, target.y * world.icon_size) + else + // rotate 30 degrees. Alternate between rotating left and right. + var/vector/v = vectorRotate(vectorMultiply(diff, 1 - positions[i]), pick(30,-30)) + boltEnd = vectorAdd(boltStart, v) + + var/bolt/bolt = new (boltStart, boltEnd) + bolt.fade = fade + bolts += bolt + + /** + * Returns a list of turfs between the bolt's starting vector to the bolt's end vector without including branched bolts + * because this only checks first and last vectors it returns a form of line between both vectors and can be inaccurate if bolt segments stray too far + * It can return null if no turfs are found. + * + * @param z the map z level to search + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum, it should be noted even at 1 this will not be too accurate, use GetAllTurfs() for a more accurate result + * @return a partial list of turfs the main bolt passes on + */ + GetTurfs(z, accurate = 16) + var/bolt/b = bolts[1] + var/line/l = b.segments[1] + + return vectorGetTurfs(l.A, end, z, accurate) + + /** + * Returns a list of turfs between the bolt's starting vector to the bolt's end vector checking all segments including all branched bolts + * It can return null if no turfs are found. + * + * @param z the map z level to search + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum + * @return a list of turfs the bolts pass on + */ + GetAllTurfs(z, accurate = 16) + var/list/locs = list() + + for(var/bolt/b in bolts) + locs = locs|b.GetAllTurfs(z, accurate) + + return locs.len ? locs : null \ No newline at end of file diff --git a/code/_helpers/LightningLib/LICENSE b/code/_helpers/LightningLib/LICENSE new file mode 100644 index 0000000000..9591157b0e --- /dev/null +++ b/code/_helpers/LightningLib/LICENSE @@ -0,0 +1,662 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. + diff --git a/code/_helpers/LightningLib/Lightning.dme b/code/_helpers/LightningLib/Lightning.dme new file mode 100644 index 0000000000..4df938a3b6 --- /dev/null +++ b/code/_helpers/LightningLib/Lightning.dme @@ -0,0 +1,31 @@ +// DM Environment file for TheLibrary.dme. +// All manual changes should be made outside the BEGIN_ and END_ blocks. +// New source code should be placed in .dm files: choose File/New --> Code File. + +// BEGIN_INTERNALS +// END_INTERNALS + +// BEGIN_FILE_DIR +#define FILE_DIR . +#define FILE_DIR "demo" +// END_FILE_DIR + +// BEGIN_PREFERENCES +// END_PREFERENCES + +// BEGIN_INCLUDE +#include "Bar.dm" +#include "Beam.dm" +#include "Bolt.dm" +#include "BranchedBolt.dm" +#include "Line.dm" +#include "math.dm" +#include "segment.dm" +#include "Vector.dm" +#ifdef __MAIN__ // BEGIN_DEMO +#include "demo\demo.dm" +#include "demo\force.dm" +#include "demo\map.dmm" +#endif // END_DEMO +// END_INCLUDE + diff --git a/code/_helpers/LightningLib/Line.dm b/code/_helpers/LightningLib/Line.dm new file mode 100644 index 0000000000..6368bc5459 --- /dev/null +++ b/code/_helpers/LightningLib/Line.dm @@ -0,0 +1,98 @@ +/** + * a line drawn between two vectors + */ + +line + var + vector + A + B + + /** + * Constructs the line, assigns both vectors A and B + * + * @param a first vector + * @param b second vector + */ + New(a, b) + ..() + + A = a + B = b + + proc + /** + * Draws a line of type with a color between two assigned vectors on z or client screen + * + * @param z the map z level to draw on, if z is client it will draw on client screen, if z is an object, it will be added as an overlay + * @param type basic segment to use when drawing + * @param color color of the segment + * @param thickness thickness of the segment + * @return an object or an image of given type transformed into a line between defined vectors + */ + Draw(z = 1, type = /obj/segment, color = "#fff", thickness = 1) + var/vector/tangent = vectorSubtract(B, A) + var/rotation = __atan2(tangent.Y, tangent.X) - 90 + + var/newWidth = tangent.Length() + var/newX = (newWidth - 1)/2 + + var/matrix/m = turn(matrix(newWidth, 0, newX, 0, thickness, 0), rotation) + + if(istype(z, /atom/movable)) + var/image/i = new (type) + + var/atom/movable/parent = z + m.Translate(A.X - (parent.pixel_x + parent.x * world.icon_size), A.Y - (parent.pixel_y + parent.y * world.icon_size)) + i.transform = m + + return i.appearance + else + + var/obj/o = new type + + var/offsetX = A.X % world.icon_size + var/offsetY = A.Y % world.icon_size + + var/x = (A.X - offsetX) / world.icon_size + var/y = (A.Y - offsetY) / world.icon_size + + o.transform = m + o.color = color + o.alpha = 255 + + if(isnum(z)) + o.loc = locate(x, y, z) + o.pixel_x = offsetX + o.pixel_y = offsetY + + else + var/client/c = z + o.screen_loc = "[x]:[offsetX],[y]:[offsetY]" + c.screen += o + + + return o + + /** + * Returns a list of turfs the line passes through + * returns null if no turfs are found + * + * @param z the map z level to search + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum + * @return a list of turfs the line passes on + */ + GetTurfs(z, accurate = 16) + return vectorGetTurfs(A, B, z, accurate) + + /** + * Rotates the line + * + * @param angle the angle to rotate thel line by + */ + Rotate(angle) + var/vector/diff = vectorSubtract(B, A) + var/vector/rotatedB = vectorRotate(diff, angle) + + B = vectorAdd(rotatedB, A) \ No newline at end of file diff --git a/code/_helpers/LightningLib/README.md b/code/_helpers/LightningLib/README.md new file mode 100644 index 0000000000..8bd931d9e4 --- /dev/null +++ b/code/_helpers/LightningLib/README.md @@ -0,0 +1,6 @@ +Lightning - README +-------------------------------------------------------------------------------- +Lightning can create a 2D line, lightning bolt or branched lightning bolt between two vectors +Written in DM language with the BYOND engine +(http://byond.com) +(http://www.byond.com/developer/Rotem12/Lightning) diff --git a/code/_helpers/LightningLib/Vector.dm b/code/_helpers/LightningLib/Vector.dm new file mode 100644 index 0000000000..f1d09f5927 --- /dev/null +++ b/code/_helpers/LightningLib/Vector.dm @@ -0,0 +1,149 @@ +/** + * a simple 2D vector + */ + +vector + var + X + Y + + /** + * Constructs the vector, assigns X and Y. + * + * @param x the vector's x + * @param y the vector's y + */ + New(x, y) + ..() + + X = x + Y = y + + + proc + /** + * Returns the length of the vector + * @return the length of the vector + */ + Length() + return max(sqrt(X*X + Y*Y), 1) + + /** + * Rounds the vectors coordinates + */ + Round(r = 1) + X = round(X, r) + Y = round(Y, r) + + /** + * Returns the start location turf of vector on z level + * @param the z level + * @return the start location turf of vector on z level + */ + Locate(z) + var/offsetX = X % world.icon_size + var/offsetY = Y % world.icon_size + + var/x = (X - offsetX) / world.icon_size + var/y = (Y - offsetY) / world.icon_size + + return locate(x, y, z) + + +proc + /** + * Returns true if vector + * + * @param v value to check + * @return if value is vector or not + */ + isVector(v) + return istype(v, /vector) + + /** + * Returns a new vector equal to the sum of the two vectors + * + * @param v1 first vector + * @param v2 second vector + * @return new vector sum of the vectors added + */ + vectorAdd(vector/v1, vector/v2) + return new /vector (v1.X + v2.X, v1.Y + v2.Y) + /** + * Returns a new vector equal to second vector subtracted from first + * + * @param v1 first vector to subtract from + * @param v2 second vector + * @return new vector sum of second vector subtracted from first + */ + vectorSubtract(vector/v1, vector/v2) + return new/vector(v1.X - v2.X, v1.Y - v2.Y) + + /** + * Return new vector from the result of multiplying the vector by a factor + * + * @param v vector to multiply + * @param num number to multiply by + * @return new vector multiplied by number + */ + vectorMultiply(vector/v, num) + return new/vector(v.X * num, v.Y * num) + + /** + * Returns the distance between two vectors + * + * @param v1 first vector + * @param v2 second vector + * @return distance between the two vectors + */ + vectorDistance(vector/v1, vector/v2) + return sqrt((v1.X - v2.X) ** 2 + (v1.Y - v2.Y) ** 2) + + /** + * Returns new normalized vector + * + * @param v vector to normalize + * @return new normalized vector + */ + vectorNormalize(vector/v) + return new/vector(v.X / v.Length(), v.Y / v.Length()) + + /** + * Rotates a vector by an angle + * + * @param v the vector to rotate + * @param angle the angle to rotate by + * @return new rotated vector + */ + vectorRotate(vector/v, angle) + return new/vector(v.X * cos(angle) - v.Y * sin(angle), v.X * sin(angle) + v.Y * cos(angle)) + + /** + * Returns a list of turfs between both vectors + * returns null if no turfs are found + * + * @param start first vector + * @param end second vector + * @param z the z level on the map + * @param accurate controlls the accurecy of this function, lower number means more accurate results however it reduces performance + * 1 being the minimum + * @return a list of turfs between two vectors + */ + vectorGetTurfs(vector/start, vector/end, z, accurate = 16) + var/list/locs + var/distance = vectorDistance(start, end) + var/vector/diff = vectorSubtract(end, start) + + for(var/i = 1 to distance step accurate) + var/x = (start.X + diff.X * (i / distance)) / world.icon_size + var/y = (start.Y + diff.Y * (i / distance)) / world.icon_size + + var/turf/t = locate(x, y, z) + + if(t) + if(!locs) + locs = list(t) + else if(!(t in locs)) + locs += t + + return locs diff --git a/code/_helpers/LightningLib/demo/1.dmi b/code/_helpers/LightningLib/demo/1.dmi new file mode 100644 index 0000000000..5bff83088a Binary files /dev/null and b/code/_helpers/LightningLib/demo/1.dmi differ diff --git a/code/_helpers/LightningLib/demo/demo.dm b/code/_helpers/LightningLib/demo/demo.dm new file mode 100644 index 0000000000..d03a9b897d --- /dev/null +++ b/code/_helpers/LightningLib/demo/demo.dm @@ -0,0 +1,82 @@ +#define DEBUG + +world + fps = 20 + icon_size = 32 + +turf + grass + icon = '1.dmi' + icon_state = "grass" + + + +mob + icon = '1.dmi' + icon_state = "person" + + Stat() + stat("CPU:", world.cpu) + + verb + setColor(i_Color as color) + + c = i_Color + + verb + setMode() + mode++ + if(mode == 5) + mode = 0 + + if(mode == 0) + world << "Draw lines" + lines = list() + if(mode == 1) + world << "Draw beams" + clear() + else if(mode == 2) + world << "Draw bolts" + else if(mode == 3) + world << "Draw branched bolts" + else if(mode == 4) + world << "Draw branched bolts towards near mobs" + +var/list/lines +proc/clear() + for(var/obj/segment/s in lines) + s.loc = null + +var/c = "#E4CCFF" +var/mode = 2 + +turf + Click() + var/vector/start = new (usr.x * world.icon_size, usr.y * world.icon_size) + var/vector/dest = new (src.x * world.icon_size, src.y * world.icon_size) + + if(mode == 0) + + var/line/l = new(start, dest) + lines += l.Draw(usr.z, color = c) + + else if(mode == 1) + + var/beam/b = new(start, dest, 50) + b.Draw(usr.z, color = c, thickness = 3) + + else if(mode == 2) + + var/bolt/b = new(start, dest, 50) + b.Draw(usr.z, color = c) + + else if(mode == 3) + + var/BranchedBolt/b = new(start, dest, 50) + b.Draw(usr.z, color = c) + + else if(mode == 4) + + var/BranchedBolt/b = new(start, dest, 50, ohearers()) + b.Draw(usr.z, color = c) + diff --git a/code/_helpers/LightningLib/demo/force.dm b/code/_helpers/LightningLib/demo/force.dm new file mode 100644 index 0000000000..b61bebefb3 --- /dev/null +++ b/code/_helpers/LightningLib/demo/force.dm @@ -0,0 +1,61 @@ +bolt/force + Effect(obj/o) + set waitfor = 0 + + // delayed spawn + o.alpha = rand(0, 40) + sleep(rand(0, 3)) + o.alpha = 255 + + animate(o, alpha = 0, time = 255 / rand(fade * 0.8, fade * 1.2), loop = -1) + + +mob + var/tmp/list/attached + + mouse_over_pointer = MOUSE_HAND_POINTER + + proc + refresh() + for(var/mob/m in attached) + detach(m) + attach(m) + + attach(mob/m) + var/vector/start = new (src.x * world.icon_size + world.icon_size / 2, src.y * world.icon_size + world.icon_size / 2) + var/vector/dest = new (m.x * world.icon_size + world.icon_size / 2, m.y * world.icon_size + world.icon_size / 2) + + var/list/bolts = list() + for(var/i = 1 to 4) + + var/bolt/force/b = new(start, dest, 50) + b.Draw(usr.z, color = c) + + bolts += b.lastCreatedBolt + + if(!attached) attached = list() + attached[m] = bolts + + detach(mob/m) + for(var/obj/o in attached[m]) + o.loc = null + attached[m] = null + + attached -= m + + if(!usr.attached.len) usr.attached = null + + Click() + if(src == usr) return + + if(usr.attached && (src in usr.attached)) + usr.detach(src) + return + + usr.attach(src) + + Move() + ..() + + if(attached) + refresh() \ No newline at end of file diff --git a/code/_helpers/LightningLib/demo/map.dmm b/code/_helpers/LightningLib/demo/map.dmm new file mode 100644 index 0000000000..18c1b47cf2 --- /dev/null +++ b/code/_helpers/LightningLib/demo/map.dmm @@ -0,0 +1,25 @@ +"a" = (/turf/grass,/area) +"b" = (/mob,/turf/grass,/area) + +(1,1,1) = {" +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaabaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaabaaaaaaabaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaabaaaaabaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaa +"} diff --git a/code/_helpers/LightningLib/math.dm b/code/_helpers/LightningLib/math.dm new file mode 100644 index 0000000000..a22245eadb --- /dev/null +++ b/code/_helpers/LightningLib/math.dm @@ -0,0 +1,13 @@ +/** + * math functions + */ + +proc + __atan2(x,y) + return (x||y)&&(y>=0 ? arccos(x/sqrt(x*x+y*y)) : 360-arccos(x/sqrt(x*x+y*y))) + + + __rand(min, max, precision = 3) + var/d = 10 ** precision + + return rand(min * d, max * d) / d \ No newline at end of file diff --git a/code/_helpers/LightningLib/segment.dm b/code/_helpers/LightningLib/segment.dm new file mode 100644 index 0000000000..585dce0169 --- /dev/null +++ b/code/_helpers/LightningLib/segment.dm @@ -0,0 +1,8 @@ +/** + * default line segment + */ + + +obj + segment + icon = 'icons/effects/segment.dmi' \ No newline at end of file diff --git a/code/_helpers/LightningLib/segment.dmi b/code/_helpers/LightningLib/segment.dmi new file mode 100644 index 0000000000..c6d1110e54 Binary files /dev/null and b/code/_helpers/LightningLib/segment.dmi differ diff --git a/code/controllers/Processes/Shuttle.dm b/code/controllers/Processes/Shuttle.dm index 32936abdc4..83da5462b2 100644 --- a/code/controllers/Processes/Shuttle.dm +++ b/code/controllers/Processes/Shuttle.dm @@ -6,4 +6,4 @@ shuttle_controller = new /datum/controller/process/Shuttle/doWork() - shuttle_controller.process() + shuttle_controller.process() \ No newline at end of file diff --git a/code/controllers/Processes/fusion.dm b/code/controllers/Processes/fusion.dm new file mode 100644 index 0000000000..da6caf954f --- /dev/null +++ b/code/controllers/Processes/fusion.dm @@ -0,0 +1,25 @@ +var/global/list/fusion_controllers = list() + +/datum/controller/process/fusion/setup() + name = "fusion controller" + schedule_interval = 5 // every 0.5 seconds + + if(!fusion_controllers) + fusion_controllers = new() + +/datum/controller/process/fusion/doWork() + for(var/datum/fusion_controller/c in fusion_controllers) + c.process() + +/datum/controller/process/fusion_ball/setup() + name = "fusion_ball" + schedule_interval = 5 // every 0.5 seconds + if(isnull(fusion_balls)) + fusion_balls = list() + +/datum/controller/process/fusion_ball/doWork() + if(fusion_balls.len > 0) + for(var/obj/fusion_ball/ball in fusion_balls) + ball.process() + else + disabled = 1 \ No newline at end of file diff --git a/code/datums/wires/arc_emitter_wires.dm b/code/datums/wires/arc_emitter_wires.dm new file mode 100644 index 0000000000..e7655529b5 --- /dev/null +++ b/code/datums/wires/arc_emitter_wires.dm @@ -0,0 +1,33 @@ +/datum/wires/arc_emitter + + holder_type = /obj/machinery/power/arc_emitter + wire_count = 3 + + var/const/SABOTAGE_WIRE = 1 + var/const/TOGGLE_WIRE = 2 + +/datum/wires/arc_emitter/GetInteractWindow() + var/obj/machinery/power/arc_emitter/A = holder + . += ..() + . += "
The red light is [A.disabled ? "off" : "on"]." + +/datum/wires/arc_emitter/CanUse() + var/obj/machinery/power/arc_emitter/A = holder + if(A.panel_open) + return 1 + return 0 + + +/datum/wires/arc_emitter/UpdateCut(index, mended) + var/obj/machinery/power/arc_emitter/A = holder + switch(index) + if(SABOTAGE_WIRE) + A.disabled = !mended + +/datum/wires/arc_emitter/UpdatePulsed(index) + if(IsIndexCut(index)) + return + var/obj/machinery/power/arc_emitter/A = holder + switch(index) + if(TOGGLE_WIRE) + A.activate(user) \ No newline at end of file diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index 0d80f080db..847e281596 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -115,6 +115,9 @@ A.blob_act() return 1 + proc/take_damage(damage) + health -= (damage) + update_icon() ex_act(severity) var/damage = 50 diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 0d0be91740..b26fc941e5 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -47,6 +47,12 @@ canister_color = "orange" can_label = 0 +/obj/machinery/portable_atmospherics/canister/hydrogen + name = "Canister \[Hydrogen\]" + icon_state = "white" + canister_color = "white" + can_label = 0 + /obj/machinery/portable_atmospherics/canister/carbon_dioxide name = "Canister \[CO2\]" icon_state = "black" @@ -344,6 +350,13 @@ update_flag return 1 +/obj/machinery/portable_atmospherics/canister/hydrogen/New() + ..() + + src.air_contents.adjust_gas("hydrogen", MolesForPressure()) + src.update_icon() + return 1 + /obj/machinery/portable_atmospherics/canister/phoron/New() ..() diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 144d72a544..5b171b64f6 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -111,6 +111,48 @@ steam.start() -- spawns the effect spawn(20) qdel(steam) +///////////////////////////////////////////// +//Plasma ball effect, standart blue +///////////////////////////////////////////// + +/obj/effect/effect/plasma_ball + name = "Lightning Bolt" + icon_state = "bluespace_blue" + anchored = 1.0 + mouse_opacity = 0 + +//Colors given as rgb value +/obj/effect/effect/plasma_ball/proc/set_color(color) + if(isnull(color)) + return + icon_state = "bluespace_grey" + src.color = color + +/obj/effect/effect/plasma_ball/New() + ..() + playsound(src.loc, "sparks", 75, 1) + var/turf/T = src.loc + if (istype(T, /turf)) + T.hotspot_expose(1000,100) + spawn (15) + qdel( src ) + return + +///////////////////////////////////////////// +//Lighting bolt between scourse and target +///////////////////////////////////////////// + +/datum/effect/effect/system/lightning_bolt + +/datum/effect/effect/system/lightning_bolt/New() + ..() +/datum/effect/effect/system/lightning_bolt/start(atom/scourse, atom/target, size = 2, sx_offset = 0, sy_offset = 0, dx_offset = 0, dy_offset = 0) + var/vector/start = new (scourse.x * world.icon_size + world.icon_size/2 + sx_offset, scourse.y * world.icon_size + world.icon_size/2 + sy_offset) + var/vector/dest = new (target.x * world.icon_size + world.icon_size/2 + dx_offset, target.y * world.icon_size + world.icon_size/2 + dy_offset) + var/bolt/b = new(start, dest, 50) + b.Draw(scourse.z, color = "#ffffff", thickness = size, split = 1) + + ///////////////////////////////////////////// //SPARK SYSTEM (like steam system) // The attach(atom/atom) proc is optional, and can be called to attach the effect diff --git a/code/game/objects/items/stacks/sheets/alloy.dm b/code/game/objects/items/stacks/sheets/alloy.dm index d4262f5603..f702e4a470 100644 --- a/code/game/objects/items/stacks/sheets/alloy.dm +++ b/code/game/objects/items/stacks/sheets/alloy.dm @@ -29,14 +29,18 @@ var/list/materials = list() var/unique_id = "" var/list/effects = list() + var/mineral + var/base -/obj/item/stack/sheet/alloy/New(var/list/comp) +/obj/item/stack/sheet/alloy/New(var/list/comp, var/mineral, var/base) ..() if(!comp) usr << "This should never appear. There HAS to be a composition list!" return materials = comp.Copy() + src.mineral = mineral + src.base = base var/pre = "" var/post = "" @@ -121,7 +125,7 @@ new/datum/stack_recipe("RUST fuel compressor frame", /obj/item/rust_fuel_compressor_frame, 12, time = 50, one_per_turf = 1), \ new/datum/stack_recipe("knife grip", /obj/item/butterflyhandle, 4, time = 20, one_per_turf = 0, on_floor = 1)) -/obj/item/stack/sheet/alloy/plasteel/New(var/list/comp) +/obj/item/stack/sheet/alloy/plasteel/New(var/list/comp, var/mineral, var/base) if(!comp || !istype(comp)) comp = list("platinum" = 0.5, "metal" = 0.5) - ..(comp) + ..(comp, mineral, base) \ No newline at end of file diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 3a7efa185a..12061f48af 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -3,7 +3,8 @@ * Oxygen * Anesthetic * Air - * Phoron + * Phorony + * Hydrogen * Emergency Oxygen */ @@ -76,8 +77,24 @@ src.air_contents.adjust_multi("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, "nitrogen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD) return +/* + * Hydrogen + */ +/obj/item/weapon/tank/hydrogen + name = "Fusion Fuel Tank" + desc = "Contains Hydrogen for use in a Fusion Reactor. Warning: extremely flammable." + icon_state = "hydrogen" + //item_state = "phoron_tank" //What does thie even do ? + flags = CONDUCT + slot_flags = null //they have no straps! +/obj/item/weapon/tank/hydrogen/New() + ..() + + src.air_contents.adjust_gas("hydrogen", 30, 1) //Needs 30 each for 4 tanks + return + /* * Phoron */ diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index e5139329a9..1fcc159b62 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -251,6 +251,7 @@ BLIND // can't see anything var/light_applied var/brightness_on var/on = 0 + var/insulating = 0 /obj/item/clothing/head/New() ..() diff --git a/code/modules/clothing/spacesuits/rig/suits/ert.dm b/code/modules/clothing/spacesuits/rig/suits/ert.dm index d3db4baf84..880df64b2f 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert.dm @@ -1,6 +1,7 @@ /obj/item/clothing/head/helmet/space/rig/ert light_overlay = "helmet_light_dual" camera_networks = list("ERT","SS13") + siemens_coefficient = 0.9 /obj/item/weapon/rig/ert name = "ERT-C hardsuit control module" diff --git a/code/modules/clothing/spacesuits/void/station.dm b/code/modules/clothing/spacesuits/void/station.dm index f8546aad6e..d440858988 100644 --- a/code/modules/clothing/spacesuits/void/station.dm +++ b/code/modules/clothing/spacesuits/void/station.dm @@ -6,6 +6,7 @@ icon_state = "rig0-engineering" item_state = "eng_helm" armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 80) + siemens_coefficient = 0.9 /obj/item/clothing/suit/space/void/engineering name = "engineering voidsuit" @@ -15,6 +16,7 @@ slowdown = 1 armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 80) allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd) + siemens_coefficient = 0.9 //Mining rig /obj/item/clothing/head/helmet/space/void/mining @@ -96,6 +98,7 @@ armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 50) max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE light_overlay = "helmet_light_dual" + siemens_coefficient = 0.9 /obj/item/clothing/suit/space/void/atmos desc = "A special suit that protects against hazardous, low pressure environments. Has improved thermal protection and minor radiation shielding." diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index 037eb6833e..499a7575ac 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -25,7 +25,6 @@ flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL siemens_coefficient = 0.9 - //Standard biosuit, orange stripe /obj/item/clothing/head/bio_hood/general icon_state = "bio_general" diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 4b4464143b..532aaa6bfd 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -91,7 +91,7 @@ flags = HEADCOVERSEYES|HEADCOVERSMOUTH|BLOCKHAIR body_parts_covered = HEAD|FACE|EYES armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) - + siemens_coefficient = 0.9 /obj/item/clothing/suit/radiation name = "Radiation suit" @@ -106,3 +106,4 @@ slowdown = 1.5 armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) flags_inv = HIDEJUMPSUIT|HIDETAIL + siemens_coefficient = 0.9 \ No newline at end of file diff --git a/code/modules/power/fusion/arc_emitter.dm b/code/modules/power/fusion/arc_emitter.dm new file mode 100644 index 0000000000..8e2925ba84 --- /dev/null +++ b/code/modules/power/fusion/arc_emitter.dm @@ -0,0 +1,300 @@ +#define ARC_EMITTER_POWER_MIN 10 // 10kW +#define ARC_EMITTER_POWER_MAX 60 // 60kW +/obj/machinery/power/arc_emitter + name = "Arc Emitter" + desc = "It is a heavy duty industrial tesla emitter." + icon = 'icons/obj/fusion.dmi' + icon_state = "arc_emitter_off" + anchored = 0 + density = 1 + req_access = list(access_engine_equip) + use_power = 0 //uses powernet power, not APC power + + active_power_usage = ARC_EMITTER_POWER_MAX * 1000 * ( 2/3 ) //40kW of fuck you + + var/active = 0 + var/powered = 0 + var/state = 2 + anchored = 1 + var/locked = 0 + var/datum/wires/arc_emitter/wires = null + var/disabled = 0 + var/arc_power = 40 + +/obj/machinery/power/arc_emitter/New() + update_icon() + ..() + +/obj/machinery/power/arc_emitter/initialize() + ..() + if(state == 2 && anchored) + connect_to_network() + wires = new(src) + +/obj/machinery/power/arc_emitter/update_icon() + if (active && avail(active_power_usage)) + icon_state = "arc_emitter_active" + else if (avail(active_power_usage)) + icon_state = "arc_emitter_on" + else + icon_state = "arc_emitter_off" + +/obj/machinery/power/arc_emitter/process() + if(stat & (BROKEN)) + return + update_icon() + if(src.state != 2 || !avail(active_power_usage)) + src.active = 0 + return + update_icon() + if(active) + fire_bolt() + update_icon() + +//Fire bolt at a target +/obj/machinery/power/arc_emitter/proc/fire_bolt() + //Shock unprotected humans + var/list/targets = list() + for(var/mob/living/carbon/human/M in oview(src, 5)) + if(!insulated(M)) + targets += M + if(targets.len > 0) + for(var/i=0, i <= 10, i+=5) + var/mob/living/carbon/human/M = pick(targets) + spawn(rand(0, 10)) + arc(M) + M.apply_damage(rand(arc_power-30, arc_power-20), damagetype = BURN) + M.apply_effect(rand(5, 10), effecttype = STUN) + new/obj/effect/effect/sparks(get_turf(M)) + return + + //Shock any blobs + for(var/obj/effect/blob/B in oview(src, 5)) + targets += B + if(targets.len > 0) + for(var/i=0, i <= 10, i+=5) + var/obj/effect/blob/B = pick(targets) + spawn(rand(0, 10)) + arc(B) + B.take_damage(arc_power/6) //diveded by 6 to get a max of 10 damage which will kill a standart blob in 3 shots. + return + + //Shock any fusion cores + for(var/obj/machinery/power/fusion/core/C in oview(src, 5)) + targets += C + if(targets.len > 0) + for(var/i=0, i <= 10, i+=5) + var/obj/machinery/power/fusion/core/C = pick(targets) + spawn(rand(0, 10)) + arc(C) + c_energize(C) + return + + //Shock any supermatter crystals + for(var/obj/machinery/power/supermatter/S in oview(src, 5)) + targets += S + if(targets.len > 0) + for(var/i=0, i <= 10, i+=5) + var/obj/machinery/power/supermatter/S = pick(targets) + spawn(rand(0, 10)) + arc(S) + s_energize(S) + return + + //Shock any other machines + for(var/obj/machinery/M in oview(src, 5)) + targets += M + if(targets.len > 0) + for(var/i=0, i <= 10, i+=5) + var/obj/machinery/M = pick(targets) + spawn(rand(i, i+5)) + arc(M) + emp(M) + +//Shoot a bolt from self to C +/obj/machinery/power/arc_emitter/proc/arc(obj/T) + if(isnull(T)) + return + var/datum/effect/effect/system/lightning_bolt/bolt = PoolOrNew(/datum/effect/effect/system/lightning_bolt) + bolt.start(src, T, size = 1, sy_offset = rand(8, 11), dx_offset = rand(-5,5), dy_offset = rand(-5,5)) + playsound(src.loc, pick( 'sound/effects/electr1.ogg', 'sound/effects/electr2.ogg', 'sound/effects/electr3.ogg'), 100, 1) + +//EMP at given obj +/obj/machinery/power/arc_emitter/proc/emp(obj/m) + empulse(get_turf(m), 1, 1) + +//Energize given core +/obj/machinery/power/arc_emitter/proc/c_energize(obj/machinery/power/fusion/core/c) + c.heat += (arc_power*c.beam_coef*5) //*5 because this ticks SO MUCH SLOWER ! + +/obj/machinery/power/arc_emitter/proc/s_energize(obj/machinery/power/supermatter/s) + s.arc_act(arc_power) + +//Check if given mob is wearing insulated cloathing +/obj/machinery/power/arc_emitter/proc/insulated(var/mob/living/carbon/human/m) + if(isnull(m.head) || isnull(m.wear_suit)) + return 0 + if(!m.head.siemens_coefficient >= 0.9 || !m.wear_suit.siemens_coefficient >= 0.9) + return 0 + return 1 + +//Attackby deals with wrenching, welding emagging ect.. +/obj/machinery/power/arc_emitter/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/weapon/wrench)) + if(active) + user << "Turn off [src] first." + return + switch(state) + if(0) + if(istype(loc, /turf/space)) + user << "You try to secure the bolts to space. It doesn't work out too well." + return + + state = 1 + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + user.visible_message("[user.name] secures [src] to the floor.", \ + "You secure the external reinforcing bolts to the floor.", \ + "You hear a ratchet") + src.anchored = 1 + if(1) + state = 0 + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + user.visible_message("[user.name] unsecures [src] reinforcing bolts from the floor.", \ + "You undo the external reinforcing bolts.", \ + "You hear a ratchet") + src.anchored = 0 + if(2) + user << "\The [src] needs to be unwelded from the floor." + return + + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(active) + user << "Turn off [src] first." + return + switch(state) + if(0) + user << "\The [src] needs to be wrenched to the floor." + if(1) + if (WT.remove_fuel(0,user)) + playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + user.visible_message("[user.name] starts to weld [src] to the floor.", \ + "You start to weld [src] to the floor.", \ + "You hear welding") + if (do_after(user,20)) + if(!src || !WT.isOn()) return + state = 2 + user << "You weld [src] to the floor." + connect_to_network() + else + user << "You need more welding fuel to complete this task." + if(2) + if (WT.remove_fuel(0,user)) + playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + user.visible_message("[user.name] starts to cut [src] free from the floor.", \ + "You start to cut [src] free from the floor.", \ + "You hear welding") + if (do_after(user,20)) + if(!src || !WT.isOn()) return + state = 1 + user << "You cut [src] free from the floor." + disconnect_from_network() + else + user << "You need more welding fuel to complete this task." + return + + if(istype(W, /obj/item/weapon/screwdriver)) + default_deconstruction_screwdriver(user,icon_state,icon_state,W) + return + + if(istype(W, /obj/item/device/multitool) || istype(W, /obj/item/weapon/wirecutters)) + if(emagged) + user << "The power control circuitry is fried." + return + if(panel_open == 1) + wires.Interact(user) + message_admins("[key_name(user, user.client)] is messing with Arc emitter wires. (?) in ([x],[y],[z] - JMP)",0,1) + log_game("[user.ckey] is messing with Arc emitter wires. ([user]) in ([x],[y],[z])") + investigate_log("wires where messed with by off by [user.key]","singulo") + return + if(locked) + user << "The controls are locked!" + return + + var/new_power = input("Set arc emitter power in kW", "Arc Emitter power", active_power_usage / 1000) as num|null + if(!new_power) + return + + arc_power = Clamp(new_power, ARC_EMITTER_POWER_MIN, ARC_EMITTER_POWER_MAX) + active_power_usage = arc_power * 1000 + + user << "You set the arc emitter's bolt strength to [arc_power]kW." + desc = "It is a heavy duty industrial arc emitter. The power dial is set to [arc_power]kW." + return + + if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda)) + if(emagged) + user << "The lock seems to be broken." + return + if(src.allowed(user)) + if(active) + src.locked = !src.locked + user << "The controls are now [src.locked ? "locked." : "unlocked."]" + else + src.locked = 0 //just in case it somehow gets locked + user << "The controls can only be locked when [src] is online." + else + user << "Access denied." + return + + + if(istype(W, /obj/item/weapon/card/emag) && !emagged) + locked = 0 + emagged = 1 + user.visible_message("[user.name] emags [src].","You short out the lock.") + return + + ..() + return + +/obj/machinery/power/arc_emitter/attack_hand(mob/user as mob) + src.add_fingerprint(user) + activate(user) + + + +/obj/machinery/power/arc_emitter/proc/activate(mob/user as mob) + if(stat & BROKEN) + return + + if(state == 2) //Not yet wrenched and welded + if(!powernet) + user << "\The [src] isn't connected to a wire." + return 1 + if(!src.locked) + if(src.active==1) + if(disabled) + user << "You try to turn [src] off but nothing happens." + return 1 + src.active = 0 + user << "You turn off [src]." + message_admins("Arc emitter turned off by [key_name(user, user.client)](?) in ([x],[y],[z] - JMP)",0,1) + log_game("Arc emitter turned off by [user.ckey]([user]) in ([x],[y],[z])") + investigate_log("turned off by [user.key]","singulo") + + else if(avail(active_power_usage)) + if(disabled) + user << "You try to turn [src] on but nothing happens." + return 1 + src.active = 1 + user << "You turn on [src]." + message_admins("Arc emitter turned on by [key_name(user, user.client)](?) in ([x],[y],[z] - JMP)",0,1) + log_game("Arc mitter turned on by [user.ckey]([user]) in ([x],[y],[z])") + investigate_log("turned on by [user.key]","singulo") + + update_icon() + else + user << "The controls are locked!" + else + user << "\The [src] needs to be firmly secured to the floor first." + return 1 \ No newline at end of file diff --git a/code/modules/power/fusion/base.dm b/code/modules/power/fusion/base.dm new file mode 100644 index 0000000000..b3d067d9ff --- /dev/null +++ b/code/modules/power/fusion/base.dm @@ -0,0 +1,128 @@ +//The fusion Tokamak +/obj/machinery/power/fusion + density = 1 + var/damage = 0 + var/wired = 0 + var/on = 0 + var/locked = 0 + emagged = 0 + var/ready = 0 + panel_open = 1 + var/datum/fusion_controller/fusion_controller + anchored = 0 + +/obj/machinery/power/fusion/New() + update_icon() + ..() + +/obj/machinery/power/fusion/proc/spark() + // Light up some sparks + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up( 3, 1, src ) + s.start() + +//Just an interface +/obj/machinery/power/fusion/proc/status() + update_icon() + return + +/obj/machinery/power/fusion/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/weapon/wrench)) + if(locked) + user << "The anchoring bolts are magnetically locked in place." + return + if(anchored) + user << "You unanchor the bolts." + else + user << "You anchor the bolts." + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + anchored = !anchored + on = 0 + update_icon() + return + + else if(istype(W, /obj/item/weapon/screwdriver)) + if(locked) + user << "The access pannel is magnetically sealed" + return + if(panel_open) + user << "You close the access pannel" + else + user << "You open the access panel" + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + panel_open = !panel_open + update_icon() + return + + else if(istype(W, /obj/item/stack/cable_coil) && panel_open && !wired) + var/obj/item/stack/cable_coil/C = W + if(C.amount < 2) + user << "You need more wires." + return + user << "You start adding cables to the Tokamak." + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 20) && C.amount >= 2) + C.use(2) + user.visible_message(\ + "[user.name] has added cables to the Tokamak!",\ + "You add cables to the Tokamak.") + wired = 1 + update_icon() + return + + else if (istype(W, /obj/item/weapon/wirecutters) && panel_open && wired) + user << "You begin to cut the cables..." + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 50)) + new /obj/item/stack/cable_coil(loc,2) + user.visible_message(\ + "[user.name] cut the cabling inside the Tokamak.",\ + "You cut the cabling inside the Tokamak.") + wired = 0 + update_icon() + return + else if(istype(W, /obj/item/weapon/card/emag) && !emagged) + user << "You hear a click disabling the magnetic seals." + emagged = 1 + update_icon() + return + ..() + +/obj/machinery/power/fusion/verb/rotate_clock() + set category = "Object" + set name = "Rotate (Clockwise)" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 90)) + +/obj/machinery/power/fusion/verb/rotate_anticlock() + set category = "Object" + set name = "Rotate (Counterclockwise)" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, -90)) + +/obj/machinery/power/fusion/update_icon() + if(!panel_open && !on) + icon_state = initial(icon_state) + return + if(panel_open && !wired) + icon_state = "[initial(icon_state)]_open" + return + if(panel_open && wired) + icon_state = "[initial(icon_state)]_wired" + return + if(on) + icon_state = "[initial(icon_state)]_on" + return + +/obj/machinery/power/fusion/Destroy() + if(!isnull(fusion_controller)) + fusion_controller.fusion_components -= src + ..() \ No newline at end of file diff --git a/code/modules/power/fusion/computer.dm b/code/modules/power/fusion/computer.dm new file mode 100644 index 0000000000..d32ccee6c3 --- /dev/null +++ b/code/modules/power/fusion/computer.dm @@ -0,0 +1,127 @@ +// Borrows code from cloning computer and gravity controll computer. +// Handles amost all user interaction with the fusion reactor. +/obj/machinery/computer/fusion + name = "Tokamak Control Console" + icon = 'icons/obj/fusion.dmi' + icon_state = "computer" + var/datum/fusion_controller/fusion_controller + //var/obj/machinery/power/fusion/core/c + var/ctag = "" +/obj/machinery/computer/fusion/New() + fusion_controller = new() + ..() + +/obj/machinery/computer/fusion/proc/reboot() + fusion_controller = new() + +/obj/machinery/computer/fusion/proc/updatemodules() + var/tmp/obj/t_core = locate(ctag) + if(isnull(t_core) || !istype(t_core, /obj/machinery/power/fusion/core)) + return + if(fusion_controller.findComponents(t_core)) + fusion_controller.computer = src + +/obj/machinery/computer/fusion/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/fusion/attack_hand(mob/user as mob) + user.set_machine(src) + add_fingerprint(user) + + if(isnull(fusion_controller)) + fusion_controller = new() + + if(stat & (BROKEN|NOPOWER)) + return + + var/dat = "

Tokamak Control Panel

" + //dat += "Refresh" + if(fusion_controller.fusion_components.len != 13) + dat += "Connect to reactor components.
" + else + //font color=green + var/obj/machinery/power/fusion/comp + var/status + dat += "Plasma Temperature: [isnull(fusion_controller.gas_contents) ? "No Gas" : "[fusion_controller.gas_contents.temperature]"]
" + dat += "Plasma nr. Moles: [isnull(fusion_controller.gas_contents) ? "No Gas" : "[fusion_controller.gas_contents.total_moles]"] " + if(fusion_controller.gas_contents.temperature < 50000) + dat += "Drain Gas
" + else + dat += "Emergency Gas Vent
" + //Stuff for the confield percentage + dat += "Containment field status: " + dat += "[fusion_controller.confield/400]% " + if(!fusion_controller.gas && fusion_controller.gas_contents.total_moles < 1) + dat += "Reset" + //dat += "Containment field strengh: [fusion_controller.confield]
" + dat += "
Neutron rods exposure: " + dat += "-- " + dat += "-" + dat += " [fusion_controller.rod_insertion*100] % " + dat += "+ " + dat += "++
" + dat += "
Dispertion Rod Status:

" + comp = fusion_controller.fusion_components[13] + status = comp.status() + dat += status + "
" + dat += "
MCR status:
" + comp = fusion_controller.fusion_components[1] + status = comp.status() + dat += "Ring 1: [status]
" + comp = fusion_controller.fusion_components[4] + status = comp.status() + dat += "Ring 2: [status]
" + comp = fusion_controller.fusion_components[7] + status = comp.status() + dat += "Ring 3: [status]
" + comp = fusion_controller.fusion_components[10] + status = comp.status() + dat += "Ring 4: [status]
" + dat += "Containmentfield Power: [fusion_controller.conPower==1 ? "Active" : "Inactive"] - " + dat += "Toggle
" + dat += "Gas release: [fusion_controller.gas==1 ? "Open" : "Closed"] -" + dat += "Toggle
" + var/exchangers = 0 + for(var/obj/machinery/power/fusion/plasma/plasma in fusion_controller.plasma) + if(!isnull(plasma.partner)) + exchangers ++ + dat += "Thermal Containment: [fusion_controller.heatpermability==1 ? "Inactive" : "Active"] - " + dat += "Toggle
" + dat += "Exchanging with: [exchangers] Heat exchangers.
" + user << browse(dat, "window=fusiongen;size=600x750") + //onclose(user, "fusiongen") + + +/obj/machinery/computer/fusion/Topic(href, href_list) + ..() + if(href_list["reset_field"]) + fusion_controller.reset_field() + if(href_list["togglecon"]) + fusion_controller.toggle_field() + if(href_list["togglegas"]) + fusion_controller.toggle_gas() + if(href_list["findcomp"]) + updatemodules() + if(href_list["toggleheatperm"]) + fusion_controller.toggle_permability() + if(href_list["event"]) + spawn(0) + if(alert("Confrim Emergency Venting",,"Yes", "No") == "Yes") + fusion_controller.emergencyVent() + if(href_list["drain"]) + fusion_controller.drainPlasma() + if(href_list["change1"]) + fusion_controller.change_rod_insertion(-0.1) + if(href_list["change2"]) + fusion_controller.change_rod_insertion(-0.01) + if(href_list["change3"]) + fusion_controller.change_rod_insertion(0.01) + if(href_list["change4"]) + fusion_controller.change_rod_insertion(0.1) + src.updateUsrDialog() + return + +/obj/machinery/computer/fusion/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/device/multitool)) + ctag = input(user,"Input Heat Dispersion Device tag","Input Tag",null) as text|null + ..() \ No newline at end of file diff --git a/code/modules/power/fusion/controller.dm b/code/modules/power/fusion/controller.dm new file mode 100644 index 0000000000..f5e13f1c1f --- /dev/null +++ b/code/modules/power/fusion/controller.dm @@ -0,0 +1,510 @@ +/* +* Regulates the fusion engine and this components. +*/ +#define TM_SAFE_ALERT "Con" +/datum/fusion_controller + var/list/fusion_components = list() //List of components making up the fusion reactor. + var/mode = 1 //Mode, direct = 0, indirect = 1. + var/gas = 0 //Is the gas release open. + var/confield = 0 + var/conPower = 0 //Is the containment field active. + var/list/plasma = list() //List of plasma fields + var/datum/gas_mixture/gas_contents = null //Plasma must contain a mix of gasses. + var/decay_coef = 80000 //The direct heat decal is diveded by this for the actual heat decay + var/heatpermability = 0 //Do we let the heat escape/exchange with the enviroment or do we contain it. (0 = contain, 0 = exchange) + var/fusion_heat = 0 //Fusion heat generated last tick + var/datum/fusionUpgradeTable/table //Datum with gas, rod and crystal coefs + var/list/coefs //List with gas and color coefs + var/rod_coef = 0 //What effect does the rod compo do on neutron/heat generation + var/field_coef = 0 //Field coef, how much does the field regen extra + var/obj/machinery/computer/fusion/computer //The computer that this is linked to + var/set_up = 0 + var/lastwarning = 0 + var/warning_delay = 10 //10 sec between warnings + var/confield_archived = 0 + var/safe_warned = 0 + var/obj/item/device/radio/radio //For radio warnings + var/rod_insertion = 1 //How far is the rod inserted, has effect on heat, neutrons and neutron damage generation + var/message_delay + var/safe_warn + var/max_field_coef = 1 + var/event_color = "" //Color of fusion events + var/neutrondam_coef = 8 //Devide neutrons by this for damage to shields + +/datum/fusion_controller/New() + fusion_controllers += src + gas_contents = new /datum/gas_mixture() + gas_contents.volume = 240 + gas_contents.temperature = T20C + table = new() + radio = new(src) + +/datum/fusion_controller/Destroy() + qdel(radio) + ..() + +//Standart process cycle +/datum/fusion_controller/proc/process() + if(set_up) + checkComponents() + if(fusion_components.len > 0) + pass_self() + updatePlasma() + calcFusion() + calcDamage() + calcConField() + announce_warning() //Announce a warning if the confield is dropping below 50% + confield_archived = confield + updateIcons() + +//Shuts down reactor by controlled gas venting +/datum/fusion_controller/proc/emergencyVent() + if(gas_contents.temperature >= 90000) + gas_contents.temperature = 89000 + leakPlasma() + removePlasma() + +//Check the individual components for various statuses +/datum/fusion_controller/proc/checkComponents() + . = 0 + if(fusion_components.len != 13) + if(gas_contents.temperature > 90000) + critFail(pick(fusion_components)) //Easteregg for egnineers who think they are safe behind glass + else + leakPlasma() + removePlasma() + if(!isnull(computer)) + computer.reboot() + qdel(src) + return + + var/emmag_nr = 0 + for(var/obj/machinery/power/fusion/comp in fusion_components) + if(!comp.ready) + if(gas_contents.temperature > 90000) + critFail(comp) + else + leakPlasma() + removePlasma() + if(!isnull(computer)) + computer.reboot() + qdel(src) + return + if(comp.emagged) + emmag_nr ++ + if(emmag_nr >= 12) + for(var/obj/machinery/power/fusion/comp in fusion_components) + comp.locked = 0 + . = 1 + +//Update the icons +/datum/fusion_controller/proc/updateIcons() + for(var/obj/machinery/power/fusion/comp in fusion_components) + comp.update_icon() + +//Pass self to the core rod for debug +/datum/fusion_controller/proc/pass_self() + var/obj/machinery/power/fusion/core/core = fusion_components[13] + core.controller = src + +//Toggle the containment field power scourse +/datum/fusion_controller/proc/toggle_field() + conPower = !conPower + +//Toggle the gas outlet +/datum/fusion_controller/proc/toggle_gas() + gas = !gas + +//Reset the force field for maintenance +/datum/fusion_controller/proc/reset_field() + conPower = 0 + confield = 0 + +//Neutron Rod insertion percentage +/datum/fusion_controller/proc/change_rod_insertion(change) + rod_insertion = Clamp(rod_insertion + change, 0, 1) + +//Toggle the containment field heat permability +/datum/fusion_controller/proc/toggle_permability() + heatpermability = !heatpermability + for(var/obj/machinery/power/fusion/plasma/p in plasma) + p.toggle_heat_transfer() + +//Spawns the "plasma" based upon the location of the core rod. +/datum/fusion_controller/proc/generatePlasma() + var/obj/machinery/power/fusion/plasma/p + var/obj/machinery/power/fusion/core/core = fusion_components[13] + + for(var/tmp/i in list(-2, 2)) + p = PoolOrNew(/obj/machinery/power/fusion/plasma, core.loc) + //p = new(core.loc) + p.x += i + if(i == 2) + p.dir = SOUTH + if(i == -2) + p.dir = NORTH + p.fusion_controller = src + plasma.Add(p) + + for(var/tmp/i in list(-2, 2)) + //p = new(core.loc) + p = PoolOrNew(/obj/machinery/power/fusion/plasma, core.loc) + p.y += i + if(i == 2) + p.dir = EAST + if(i == -2) + p.dir = WEST + p.fusion_controller = src + plasma.Add(p) + +//Update the status of the plasma.. among other things -_-' +/datum/fusion_controller/proc/updatePlasma() + var/obj/machinery/power/fusion/core/core = fusion_components[13] + if(plasma.len == 0 && gas) + generatePlasma() + + if(gas) + pumpPlasma() + + if(plasma.len == 0 || isnull(gas_contents) || gas_contents.total_moles < 1) + return + + for(var/obj/machinery/power/fusion/comp in fusion_components) + if(comp.anchored == 0) + leakPlasma() + if(gas_contents.temperature > 90000) //We are at fusion temp EXPLODE! + critFail() + return + + if(!confield) + if(gas_contents.temperature < 75000) + leakPlasma() + removePlasma() + gas = 0 + else + critFail(pick(fusion_components)) + return + + core.decay() + + //heat up plasma and temp sanity check + var/tmp/heatdif = core.heat + //The arc emitters temerature is about 100k deg so we cap input there. + if(gas_contents.temperature >= 100000) + heatdif = 0 + core.heat -= heatdif + gas_contents.temperature += heatdif + fusion_heat - calcDecay(gas_contents.temperature) //Calculating temp change + if(gas_contents.temperature < 290) //need to do a area check here + gas_contents.temperature = 290 + gas_contents.update_values() + fusion_heat = 0 + if(gas_contents.temperature > 100000000) + gas_contents.temperature = 100000000 + gas_contents.update_values() + + if(gas_contents.temperature > 10000) + for(var/obj/machinery/power/fusion/plasma/p in plasma) + p.luminosity = min(gas_contents.temperature/7500, 2) + p.set_light(3, p.luminosity, "##00D4FF") //Light blue + //p.icon_state = "plas_stream" + else + for(var/obj/machinery/power/fusion/plasma/p in plasma) + p.icon_state = "plas_cool" + +//Pump plasma from the ring tanks into the "field" +/datum/fusion_controller/proc/pumpPlasma() + for(var/obj/machinery/power/fusion/ring_corner/r in fusion_components) + pump_gas(r, r.get_tank_content(), gas_contents, r.get_tank_moles()) + gas_contents.update_values() + coefs = table.gas_coef(gas_contents) + +//Pump plasma back into rings. +/datum/fusion_controller/proc/drainPlasma() + gas_contents.divide(4) + //world << "Total gass moles of devide mix: [gas_contents.total_moles]" + var/datum/gas_mixture/tank_mix + for(var/obj/machinery/power/fusion/ring_corner/r in fusion_components) + tank_mix = new() + tank_mix.temperature = gas_contents.temperature + tank_mix.add(gas_contents) + //world << "Total gass moles of tank mix: [tank_mix.total_moles]" + r.set_tank_content(tank_mix) + gas_contents = new() + removePlasma() + +//Remove plasma objects. +/datum/fusion_controller/proc/removePlasma() + for(var/obj/machinery/power/fusion/plasma/p in plasma) + qdel(p) + plasma = list() + +//If the containment field get disabled.. bad stuff. +//This can also be used to flood the room in case of an emerency so the reactor does not go boom. +/datum/fusion_controller/proc/leakPlasma() + var/tmp/dif = gas_contents.temperature/4 + for(var/obj/machinery/power/fusion/plasma/p in plasma) + pump_gas(p, gas_contents, p.loc.return_air(), gas_contents.total_moles/4) + gas_contents.temperature -= dif + gas = 0 + gas_contents = new() + +//Calculate plasma passive heat decay. +/datum/fusion_controller/proc/calcDecay(var/temp) + . = (2**(temp/(decay_coef)))+((0.00005*temp)**2) + +//Containment field calculations and adjustment.. also sprite overlay. +/datum/fusion_controller/proc/calcConField() + world << "Confield at calcConField [confield]" + if(confield) + for(var/obj/machinery/power/fusion/plasma/p in plasma) + p.overlays = list(image(p.icon, "field_overlay")) + for(var/obj/machinery/power/fusion/comp in fusion_components) + comp.on = 1 + comp.locked = 1 //Prevent the components from beeing wrenched out of place. + else + for(var/obj/machinery/power/fusion/plasma/p in plasma) + p.overlays.Cut() + for(var/obj/machinery/power/fusion/comp in fusion_components) + comp.on = 0 + comp.locked = 0 + + var/tmp/tmp_confield = confield + confield = 0 + for(var/obj/machinery/power/fusion/ring_corner/r in fusion_components) + r.charge() + if(conPower) + tmp_confield += r.field_energy() + if(!isnull(coefs)) + tmp_confield = tmp_confield*coefs["shield"] + tmp_confield*field_coef + confield = Clamp(tmp_confield, 0, 40000 + 1000*field_coef) + world << "Confield afther calcConField [confield]" + +//When does fusion happen ? +/datum/fusion_controller/proc/calcFusion() + if(plasma.len == 0 || isnull(gas_contents)) + return + if(gas_contents.temperature < 90000) + return + for(var/obj/machinery/power/fusion/plasma/p in plasma) + var/change = min(((gas_contents.temperature/500000)*100), 25) //This needs tweaking also with gass mixtures + change = change * coefs["fuel"] * coefs["dampening"] + if(prob(change)) + fusionEvent(p) + +//Fusion event, generates heat neutrons wich generate energy via collectors. +/datum/fusion_controller/proc/fusionEvent(obj/machinery/power/fusion/plasma/p) + //These base values should be class values ! + var/tmp/neutrons = 1000 //Base neutrons + var/tmp/heat = 2000 //Base heat + neutrons += (heat*coefs["heat_neutron"] - neutrons*coefs["neutron_heat"])*coefs["neutron"] + neutrons*rod_coef + heat += neutrons*coefs["neutron_heat"] - heat*coefs["heat_neutron"] + heat*rod_coef + fusion_heat = heat*rod_insertion + p.transfer_energy(neutrons*rod_insertion) + spawn() + p.spark() + p.set_light(3, 5, event_color) + var/tmp/obj/effect/effect/plasma_ball/pball = new(get_turf(p)) + pball.set_color(event_color) + + var/list/targets = list() + for(var/mob/living/carbon/human/M in oview(p, 5)) + if(!insulated(M)) + targets += M + if(targets.len > 0) + var/mob/living/carbon/human/M = pick(targets) + arc(M, p) + M.apply_damage(rand(10, 20), damagetype = BURN) + M.apply_effect(rand(10, 20), effecttype = STUN) + + //Neutrons effect the containment field, more neutrons = more power but also more were on the field + for(var/obj/machinery/power/fusion/ring_corner/r in fusion_components) + world << "substracting [neutrons*rod_insertion] from confield [confield]" + confield -= (neutrons/neutrondam_coef)*rod_insertion + world << "Resulting in [confield]" + + if(coefs["explosive"] && prob(5)) + critFail(p) + +/datum/fusion_controller/proc/insulated(var/mob/living/carbon/human/m) + if(isnull(m.head) || isnull(m.wear_suit)) + return 0 + if(!m.head.siemens_coefficient >= 0.9 || !m.wear_suit.siemens_coefficient >= 0.9) + return 0 + return 1 + +/datum/fusion_controller/proc/arc(obj/T, obj/S) + if(isnull(T)) + return + var/datum/effect/effect/system/lightning_bolt/bolt = PoolOrNew(/datum/effect/effect/system/lightning_bolt) + bolt.start(S, T, size = 1) + playsound(S.loc, pick( 'sound/effects/electr1.ogg', 'sound/effects/electr2.ogg', 'sound/effects/electr3.ogg'), 100, 1) + new/obj/effect/effect/sparks(get_turf(T)) + +//Calculate if we should do damage to the rings according to heat of the gas. +//A random ring will take 1 point of damage for every 5000 deg above 1 mil deg. +/datum/fusion_controller/proc/calcDamage() + if(gas_contents.temperature > 1000000) + var/i = pick(list(1,2,3,4,5,6,7,8,9,10,11,12)) + var/obj/machinery/power/fusion/component = fusion_components[i] + component.damage += (gas_contents.temperature - 1000000)/5000 + + for(var/obj/machinery/power/fusion/component in fusion_components) + if(component.damage > 500 && component.damage < 800) + //warning + + else if(component.damage > 800 && component.damage < 950) + //Critical warning + + else if(component.damage > 999) + //critically fail + critFail(component) + +//Critically fail in an explosion .. or worse. +/datum/fusion_controller/proc/critFail(var/obj/o) + if(isnull(o)) + return + if(gas_contents.temperature > 600000) + //You are really deep in the shit now boi! + new/obj/fusion_ball(o.loc) + gas = 0 + leakPlasma() + removePlasma() + spawn() + explosion(get_turf(o), 2, 4, 10, 15) + +/datum/fusion_controller/proc/announce_warning() + var/tmp/alert_msg = "Warning Tokamak containment field integrity at [round(confield/400)]%" + if(confield < 20000) + if(confield < confield_archived) // The damage is still going up sinse last calc + safe_warn = 1 + else if (safe_warn) + safe_warn = 0 // We are safe, warn only once + alert_msg = TM_SAFE_ALERT + else + alert_msg = null + if(alert_msg && world.timeofday >= message_delay) + message_delay = world.timeofday + 15 + radio.autosay(alert_msg, "Tokamak Monitor") + +//LONG LIVE SPAGETTI ! +//This finds all the components in a efficient but really clumsy code wise way. +/datum/fusion_controller/proc/findComponents(obj/machinery/power/fusion/core/c) + var/tmp/list/temp_list = list() + if(isnull(c)) + return + c.controller = src + var/obj/machinery/power/fusion/mag_ring = null + for(var/dir in list(NORTHWEST,NORTHEAST,SOUTHEAST,SOUTHWEST)) + //Getting the corner ring + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring_corner/, get_step(get_step(c, dir),dir)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring_corner) ||!isnull(mag_ring))) + return + //Getting straight rings and check dir on corner, let the spagetti begin. + if(dir == NORTHWEST) + if(!mag_ring.dir == EAST || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),NORTH)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == EAST || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),WEST)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == SOUTH || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + continue + + if(dir == NORTHEAST) + if(!mag_ring.dir == SOUTH || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),NORTH)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == WEST || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),EAST)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == SOUTH || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + continue + + if(dir == SOUTHEAST) + if(!mag_ring.dir == WEST || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),SOUTH)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == WEST || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),EAST)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == NORTH || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + continue + + if(dir == SOUTHWEST) + if(!mag_ring.dir == NORTH || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),WEST)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == NORTH || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + mag_ring = null + mag_ring = locate(/obj/machinery/power/fusion/ring/, get_step(get_step(c, dir),SOUTH)) + if(!(istype(mag_ring, /obj/machinery/power/fusion/ring) ||!isnull(mag_ring))) + return + if(!mag_ring.dir == EAST || !mag_ring.anchored == 1) + return + temp_list.Add(mag_ring) + + temp_list.Add(c) + if(temp_list.len != 13) + return + for(var/obj/machinery/power/fusion/comp in temp_list) + if(!comp.ready) + return + comp.fusion_controller = src + //Calculating component coefs + field_coef = 0 + rod_coef = 0 + for(var/obj/machinery/power/fusion/ring_corner/r in temp_list) + if(isnull(r.rod) || isnull(r.crystal)) + return + if(isnull(event_color) || event_color == "") + event_color = table.rod_color(r.rod) + else + event_color = BlendRGB(event_color, table.rod_color(r.rod), 0.5) + rod_coef += table.rod_coef(r.rod) + field_coef += table.field_coef(r.crystal) + rod_coef = rod_coef/4 + field_coef = field_coef/4 + + fusion_components = temp_list + set_up = 1 + return 1 + +/datum/fusion_controller/proc/addComp(var/obj/machinery/computer/fusion/comp) + fusion_components.Add(comp) \ No newline at end of file diff --git a/code/modules/power/fusion/core.dm b/code/modules/power/fusion/core.dm new file mode 100644 index 0000000000..681f46c93a --- /dev/null +++ b/code/modules/power/fusion/core.dm @@ -0,0 +1,59 @@ +//The fusion "core" heating rod +//What gets hit by the beam and heats up the plasma +/obj/machinery/power/fusion/core + name = "Heat Distrubtion Device" + desc = "Converts incoming energy into heat and distributes it." + icon = 'icons/obj/fusion.dmi' + icon_state = "core_off" + var/heat = 0 + var/controller + var/beam_coef = 2 + ready = 0 + var/last_power = 0 + wired = 0 + panel_open = 1 + anchored = 0 + +/obj/machinery/power/fusion/core/New() + update_icon() + ..() + +/obj/machinery/power/fusion/core/status() + return "Buildupheat: [heat]
Integrity: [(1000-damage)/10] %
Energy Level: [last_power/1000] Kw" + +//Temperature and power decay of the core +/obj/machinery/power/fusion/core/proc/decay() + //Do something with the alloy compo here + heat = max(0, heat-((0.0005*heat)**2)) + +//Hitting the core with anything, this includes power and damage calculations from the emitter. +/obj/machinery/power/fusion/core/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj, /obj/item/projectile/beam/continuous/emitter)) + var/obj/item/projectile/beam/continuous/emitter/B = Proj + heat += B.power*beam_coef + else + damage += Proj.damage + return 0 + +//Override to make sure the icon does not dissapear +/obj/machinery/power/fusion/core/update_icon() + if(last_power > 1 || heat > 1) + icon_state = "core_on" + else + icon_state = "core_off" + return + +/obj/machinery/power/fusion/core/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/device/multitool)) + src.tag = input(user,"Input Device tag","Input Tag",null) as text|null + ..() + +/obj/machinery/power/fusion/core/process() + update_icon() + decay() + +/obj/machinery/power/fusion/core/proc/receive_neutrons(var/neutrons) + var/power = 400*neutrons //Generate about 450kw at base level. + add_avail(power) + last_power = power + return \ No newline at end of file diff --git a/code/modules/power/fusion/fabricator.dm b/code/modules/power/fusion/fabricator.dm new file mode 100644 index 0000000000..56bbcd1411 --- /dev/null +++ b/code/modules/power/fusion/fabricator.dm @@ -0,0 +1,151 @@ +/obj/machinery/tokamakFabricator + name = "Tokamak Component Fabricator" + desc = "A large automated factory for producing components for the Tokamak reacor." + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 20 + active_power_usage = 5000 + + icon = 'icons/obj/machines/drone_fab.dmi' + icon_state = "drone_fab_idle" + var/obj/item/stack/sheet/alloy/alloy + var/datum/reagents/liquid + var/datum/gas_mixture/gas_contents + var/obj/item/weapon/tank/hydrogen/tank + +/obj/machinery/tokamakFabricator/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/alloy_synth(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + +/obj/machinery/tokamakFabricator/power_change() + ..() + if (stat & NOPOWER) + icon_state = "drone_fab_nopower" + +/obj/machinery/tokamakFabricator/process() + if(stat & NOPOWER) + if(icon_state != "drone_fab_nopower") + icon_state = "drone_fab_nopower" +/* + if() + icon_state = "drone_fab_idle" + return + + icon_state = "drone_fab_active" +*/ +/obj/machinery/tokamakFabricator/attackby(var/obj/O, var/mob/user) + if(istype(O, /obj/item/weapon/screwdriver)) + if(!panel_open) + panel_open = 1 + user << "You open the maintenance hatch of [src]." + else + panel_open = 0 + user << "You close the maintenance hatch of [src]." + return 1 + if(panel_open && istype(O, /obj/item/weapon/crowbar)) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(src.loc) + M.state = 2 + M.icon_state = "box_1" + for(var/obj/I in component_parts) + I.loc = src.loc + qdel(src) + return 1 + + if(istype(O, /obj/item/stack/sheet/alloy)) + var/tmp/obj/item/stack/sheet/alloy/A = O + if(isnull(O)) //Something has gone wrong + return + if(isnull(alloy)) //No alloy in the fab yet + if((A.materials[A.base]/A.materials[A.mineral]) == 1) + var/tmp/amount = min(round(input("How many sheets do you want to add?") as num), 25) + user << "You add [amount] sheets to the fabricator" + A.use(amount) + alloy = new(A.materials, A.mineral, A.base) + alloy.amount = amount + return + else + user << "The alloy does not have the right composition!" + return + else //Alloy in the fab + if(alloy.amount >= 25) //Fab is full + user << "The fabricator is already full of [alloy]!" + return + if(A.mineral == alloy.mineral && A.base == alloy.base) + var/tmp/max = 25 - alloy.amount + var/tmp/amount = min(round(input("How many sheets do you want to add?") as num), max) + user << "You add [amount] sheets to the fabricator" + A.use(amount) + alloy.amount += amount + return + ..() + +//Browser menu +/obj/machinery/tokamakFabricator/attack_hand(mob/user as mob) + user.set_machine(src) + add_fingerprint(user) + + if(stat & (BROKEN|NOPOWER)) + return + + var/dat = "

Tokamak Component Fabricator


" + dat += "Alloy: [!isnull(alloy) ? "[alloy].":"Alloy store empty."]
" + dat += "Amount: [!isnull(alloy) ? "[alloy.amount].":"0"]
" + if(!isnull(alloy)) + dat += "Produce Field Crystal
" + dat += "Produce Neutron Rod
" + dat += "Eject Alloy
" + user << browse(dat, "window=tokamakfab;size=500x500") + +//Topic by browser +/obj/machinery/tokamakFabricator/Topic(href, href_list) + ..() + if(usr.machine != src) + message_admins("Tokamak Fabricator href hacks in progress by [usr] ([x],[y],[z] - JMP)",0,1) + if(href_list["crystal"]) + produceCrystal() + if(href_list["rod"]) + produceRod() + src.updateUsrDialog() + +//produce a field crystal +/obj/machinery/tokamakFabricator/proc/produceCrystal() + if(alloy.base != "glass") + usr << "Stored alloy is of the wrong base." + return + if(alloy.amount < 5) + usr << "Not enough alloy in store." + return + alloy.use(5) + var/obj/item/weapon/shieldCrystal/crystal = new(alloy.mineral) + crystal.loc = get_turf(get_step(src, EAST)) + +//produce a neutron rod +/obj/machinery/tokamakFabricator/proc/produceRod() + if(alloy.base != "metal") + usr << "Stored alloy is of the wrong base." + return + if(alloy.amount < 5) + usr << "Not enough alloy in store.." + return + alloy.use(5) + var/obj/item/weapon/neutronRod/rod = new(alloy.mineral) + rod.loc = get_turf(get_step(src, EAST)) + +//produce a neutron rod +/obj/machinery/tokamakFabricator/proc/ejectAlloy() + if(alloy.base != "metal") + usr << "Stored alloy is of the wrong base." + return + if(alloy.amount < 5) + usr << "Not enough alloy in store.." + return + alloy.use(5) + var/obj/item/weapon/neutronRod/rod = new(alloy.mineral) + rod.loc = get_turf(usr) \ No newline at end of file diff --git a/code/modules/power/fusion/fusion_ball.dm b/code/modules/power/fusion/fusion_ball.dm new file mode 100644 index 0000000000..0d72fc3de0 --- /dev/null +++ b/code/modules/power/fusion/fusion_ball.dm @@ -0,0 +1,153 @@ +var/global/list/fusion_balls = list() +//Plasma ball singulo kind of all destructive bs kills everyone hahaha (Based upon lord singulo) +//The fusion ball makes use of Rotem12's Lighting for byond (https://github.com/Rotem12/Lightning). +/obj/fusion_ball/ + name = "Fusion Event Ball" + desc = "An out of controll fusion reaction." + icon = 'icons/rust.dmi' + icon_state = "emfield_s1" + anchored = 1 + density = 0 + layer = 6 + light_range = 6 + unacidable = 1 //Don't comment this out. + can_fall = 0 // can't fall down z-levels + var/start_time = 0 //At what time did we start + var/end_time = 0 //At what time are we going to end + var/middle_time = 0 //Right in the middle of start and end. + var/escalate = 0 //Are we at full or half size ? + var/shock_range = 10 //How far away do we sock people ? + var/kill_shock_range = 5 //How var away do we fry people ? + var/move_self = 1 //Do we move on our own? + var/target = null //Its target. Moves towards the target if it has one. + var/last_failed_movement = 0 //Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing. + var/chained = 0//Adminbus chain-grab + var/emp_change = 0 + var/x_offset = 0 + var/y_offset = 0 + +/obj/fusion_ball/New(loc) + //CARN: admin-alert for chuckle-fuckery. + admin_investigate_setup() + start_time = world.realtime + end_time = start_time + 300 + middle_time = start_time + 150 + + ..() + processScheduler.enableProcess("fusion_ball") //To make sure its not checking for balls when there are non. + fusion_balls += src + for(var/obj/machinery/power/singularity_beacon/singubeacon in machines) + if(singubeacon.active) + target = singubeacon + break + +/obj/fusion_ball/Destroy() + explosion(get_turf(src), 4, 5, 6, 6) + fusion_balls -= src + ..() + +/obj/fusion_ball/attack_hand(mob/user as mob) + kill_shock(user) + return 1 + +/obj/fusion_ball/bullet_act(obj/item/projectile/P) + return 0 //Will there be an impact? Who knows. Will we see it? No. + +/obj/fusion_ball/Bump(atom/A) + if(istype(A, /turf)) + return + if(istype(A, /mob/living)) + kill_shock(A) + return + qdel(A) + +/obj/fusion_ball/Bumped(atom/A) + if(istype(A, /turf)) + return + if(istype(A, /mob/living)) + kill_shock(A) + return + qdel(A) + +/obj/fusion_ball/process() + check_time() + move() + shock() + +/obj/fusion_ball/proc/check_time() + if(world.realtime > end_time) + Destroy() + if(world.realtime > middle_time && !escalate) + escalate() + +/obj/fusion_ball/proc/escalate() + shock_range = 15 + kill_shock_range = 5 + emp_change = 0 + icon = 'icons/effects/96x96.dmi' + icon_state = "emfield_s3" + x_offset = 32 + y_offset = 32 + +/obj/fusion_ball/attack_ai() //To prevent ais from killing itself be clicking on the ball of plasma (who would klick on a ball of plasma anyway) + return + +/obj/fusion_ball/proc/admin_investigate_setup() + message_admins("A fusion ball has spawned at: ([x], [y], [z] - JMP).") + investigate_log("A fusion ball has spawned at: ([x], [y], [z]), src") + +/obj/fusion_ball/proc/shock() + //set background = BACKGROUND_ENABLED + /* + spawn() + for(var/atom/a in orange(des_range, src)) + qdel(a) + */ + spawn() + for(var/mob/living/M in ohearers(shock_range,src)) //if you are behind glass your are safe.(this returns only mobs !) + var/dist = get_dist(M, src) + if(dist > kill_shock_range) + kill_shock(M) + continue + hurt_shock(M) + var/list/viewers = oview(shock_range,src) + //Shoot 3 emp bolts at random locations. + for(var/i = 0, i <= 3, i++) + var/obj/m = pick(viewers) + emp(m) + return + +/obj/fusion_ball/proc/emp(obj/m) + var/datum/effect/effect/system/lightning_bolt/bolt = new() + bolt.start(src, m, sx_offset = x_offset, sy_offset = y_offset) + playsound(src.loc, pick( 'sound/effects/electr1.ogg', 'sound/effects/electr2.ogg', 'sound/effects/electr3.ogg'), 100, 1) + empulse(get_turf(m), 1, 1) + +/obj/fusion_ball/proc/hurt_shock(var/mob/living/m) + if(m.status_flags & GODMODE) + return + var/datum/effect/effect/system/lightning_bolt/bolt = new() + bolt.start(src, m, sx_offset = x_offset, sy_offset = y_offset) + playsound(src.loc, pick( 'sound/effects/electr1.ogg', 'sound/effects/electr2.ogg', 'sound/effects/electr3.ogg'), 100, 1) + m.apply_damage(rand(10, 20), damagetype = BURN) + m.apply_effect(rand(10, 20), effecttype = STUN) + new/obj/effect/effect/sparks(get_turf(m)) + +/obj/fusion_ball/proc/kill_shock(var/mob/living/m) + if(m.status_flags & GODMODE) + return + var/datum/effect/effect/system/lightning_bolt/bolt = new() + playsound(src.loc, pick( 'sound/effects/electr1.ogg', 'sound/effects/electr2.ogg', 'sound/effects/electr3.ogg'), 100, 1) + bolt.start(src, m, sx_offset = x_offset, sy_offset = y_offset) + new/obj/effect/effect/sparks(get_turf(m)) + m.dust() + +/obj/fusion_ball/proc/move() + var/movement_dir = pick(alldirs) + + if(target && prob(60)) + movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one + + spawn(0) + step(src, movement_dir) + return 1 \ No newline at end of file diff --git a/code/modules/power/fusion/plasma.dm b/code/modules/power/fusion/plasma.dm new file mode 100644 index 0000000000..5e504182e9 --- /dev/null +++ b/code/modules/power/fusion/plasma.dm @@ -0,0 +1,65 @@ +/obj/machinery/power/fusion/plasma + dir = 0 + icon = 'icons/obj/fusion.dmi' + icon_state = "plas_cool" + density = 0 + var/transfering = 0 + var/datum/gas_mixture/air_contents = null + var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null + var/network = null + ready = 1 + +//When fusion happens energize the core copied over from rad collectors. +/obj/machinery/power/fusion/plasma/proc/transfer_energy(var/neurons = 0) + for(var/obj/machinery/power/fusion/core/C in fusion_controller.fusion_components) + var/distance = get_dist(C, src) + if(distance && distance <= 10) //sanity for 1/0 + //stop their being a massive benifit to moving the rad collectors closer + if(distance < 3) distance = 2.67 // between 25 - 50k benifit (level 1) + //for collectors using standard phoron tanks at 1013 kPa, the actual power generated will be this power*0.3*20*29 = power*174 + //The closer the better radiation intensity is inversely to space traveled. + C.receive_neutrons(neurons/(distance**2)) + return + +/obj/machinery/power/fusion/plasma/proc/toggle_heat_transfer() + //Init part and partner check + transfering = !transfering + if (!transfering) + if(!isnull(partner)) + partner.partner = null + partner = null + return + + if(!partner) + var/partner_connect = turn(dir, 90) + + for(var/obj/machinery/atmospherics/unary/heat_exchanger/target in get_step(src,partner_connect)) + if(target.dir & get_dir(src,target)) + partner = target + partner.partner = src + break + + if(isnull(partner)) //No partner was found :( + return 0 + air_contents = fusion_controller.gas_contents + +/obj/machinery/power/fusion/plasma/attack_hand(mob/user as mob) + var/tmp/mob/living/M = user + M.dust() + +/obj/machinery/power/fusion/plasma/bullet_act(obj/item/projectile/P) + return 0 //Will there be an impact? Who knows. Will we see it? No. + +/obj/machinery/power/fusion/plasma/Bump(atom/A) + if(istype(A, /mob/living)) + var/tmp/mob/living/M = A + M.dust() + return + +/obj/machinery/power/fusion/plasma/Bumped(atom/A) + if(istype(A, /mob/living)) + var/tmp/mob/living/M = A + M.dust() + +/obj/machinery/power/fusion/plasma/update_icon() + return \ No newline at end of file diff --git a/code/modules/power/fusion/ring.dm b/code/modules/power/fusion/ring.dm new file mode 100644 index 0000000000..e0dca7ee5f --- /dev/null +++ b/code/modules/power/fusion/ring.dm @@ -0,0 +1,175 @@ +//Four core corners (magnatic rings) +/obj/machinery/power/fusion/ring_corner + name = "Fusion Containment Ring" + desc = "Part of the fusion containment ring keeps hot fusion from escaping." + var/battery = 0 + var/integrity = 1000 + icon = 'icons/obj/fusion.dmi' + icon_state = "ring_corner" + anchored = 0 + wired = 0 + panel_open = 1 + density = 1 + use_power = 0 + var/obj/item/weapon/tank/hydrogen/tank + var/obj/item/weapon/neutronRod/rod + var/obj/item/weapon/shieldCrystal/crystal + +/obj/machinery/power/fusion/ring_corner/New() + //FOR DEBUG + tank = new() + rod = new() + crystal = new() + update_icon() + ..() + +/obj/machinery/power/fusion/ring_corner/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/weapon/tank/hydrogen)) + if(src.tank) + user << "There's already a phoron tank loaded." + return 1 + user.drop_item() + src.tank = W + W.loc = src + update_icon() + return 1 + else if(istype(W, /obj/item/weapon/crowbar)) + var/obj/item/weapon/to_eject = input("What do you want to remove?") as null|anything in list(tank,rod,crystal) + if(!to_eject) + return + if(to_eject == tank) + eject_tank() + if(to_eject == rod) + eject_rod() + if(to_eject == crystal) + eject_crystal() + update_icon() + return + if(istype(W, /obj/item/weapon/shieldCrystal)) + if(crystal) + user << "There's already a field crystal installed." + return 1 + user.drop_item() + crystal = W + W.loc = src + update_icon() + return + if(istype(W, /obj/item/weapon/neutronRod)) + if(rod) + user << "There's already an absorbtion rod installed." + return 1 + user.drop_item() + rod = W + W.loc = src + update_icon() + + ..() + +/obj/machinery/power/fusion/ring_corner/update_icon() + //Some cheaty sneeky var updating here + ..() + if(!wired || !anchored || isnull(crystal) || isnull(rod)) + ready = 0 + else + ready = 1 + +/obj/machinery/power/fusion/ring_corner/proc/eject_tank() + var/obj/item/weapon/tank/hydrogen/Z = src.tank + if (!Z) + return + Z.loc = get_turf(src) + Z.layer = initial(Z.layer) + src.tank = null + update_icon() + +/obj/machinery/power/fusion/ring_corner/proc/eject_rod() + if(check_lock()) + return + var/obj/item/weapon/neutronRod/Z = src.rod + if (!Z) + return + Z.loc = get_turf(src) + Z.layer = initial(Z.layer) + src.rod = null + update_icon() + +/obj/machinery/power/fusion/ring_corner/proc/eject_crystal() + if(check_lock()) + return + var/obj/item/weapon/shieldCrystal/Z = src.crystal + if (!Z) + return + Z.loc = get_turf(src) + Z.layer = initial(Z.layer) + src.crystal = null + update_icon() + +/obj/machinery/power/fusion/ring_corner/proc/check_lock() + if(locked) + usr << "The component is magnetically locked in place." + return 1 + return 0 + +/obj/machinery/power/fusion/ring_corner/proc/charge() + if(battery < 10000) + use_power(600) + battery = min(battery + 200, 10000) + +//Returns the field energy produced by the ring. +/obj/machinery/power/fusion/ring_corner/proc/field_energy() + if(battery > 150) + battery -= 150 + return 150 + else + return battery + battery = 0 + +//Return content of tank inside, returns an empty gas mix if there is no or no gas mix in that tank. +/obj/machinery/power/fusion/ring_corner/proc/get_tank_content() + if(isnull(tank)) + return new/datum/gas_mixture() + if(isnull(tank.air_contents)) + return new/datum/gas_mixture() + return tank.air_contents + +//Set the content of the tank +/obj/machinery/power/fusion/ring_corner/proc/set_tank_content(var/datum/gas_mixture/gas) + //world << "gas moles in set_tank_contents [gas.total_moles]" + if(gas.temperature == 0) //For some reason if I dont do this it turns the temp to 0. + gas.temperature = 293.15 + tank.air_contents = gas + //world << "gas moles in tank afther set_tank_contents [tank.air_contents.total_moles]" + +/obj/machinery/power/fusion/ring_corner/proc/get_tank_moles() + if(isnull(tank)) + return 0 + if(isnull(tank.air_contents)) + return 0 + return tank.air_contents.total_moles + +/obj/machinery/power/fusion/ring_corner/status() + return "[(1000-damage)/10] %
" + +//8 edges of the magnetic ring +/obj/machinery/power/fusion/ring + name = "Fusion Containment Ring" + desc = "Part of the fusion containment ring keeps hot fusion from escaping." + var/integrity = 1000 + icon = 'icons/obj/fusion.dmi' + icon_state = "ring" + anchored = 0 + wired = 0 + panel_open = 1 + density = 1 + use_power = 0 + +/obj/machinery/power/fusion/ring/New() + update_icon() + ..() + +/obj/machinery/power/fusion/ring/update_icon() + ..() + if(!wired || !anchored) + ready = 0 + else + ready = 1 \ No newline at end of file diff --git a/code/modules/power/fusion/upgrades.dm b/code/modules/power/fusion/upgrades.dm new file mode 100644 index 0000000000..c9ec0fdcea --- /dev/null +++ b/code/modules/power/fusion/upgrades.dm @@ -0,0 +1,105 @@ +/datum/fusionUpgradeTable + var/list/rod = list() + var/list/crystal = list() + var/list/rod_color = list() + var/const/maxfuel = 240 + +/datum/fusionUpgradeTable/New() + rod = list(\ + "iron" = 0.1,\ + "silver" = 0.2,\ + "gold" = 0.4,\ + "platinum" = 0.8,\ + "phoron" = 1.6,\ + "osmium" = 3.2,\ + "tritium" = 6.4) + + rod_color = list(\ + "iron" = "#0067FF",\ + "silver" = "#00ccff",\ + "gold" = "#ffff00",\ + "platinum" = "#00ff00",\ + "phoron" = "#a31aff",\ + "osmium" = "#ff00ff",\ + "tritium" = "#ff3300") + + crystal = list(\ + "iron" = 0.1,\ + "silver" = 0.2,\ + "gold" = 0.4,\ + "platinum" = 0.8,\ + "phoron" = 1.6,\ + "osmium" = 3.2,\ + "tritium" = 6.4) + +//Neutron & heat upgrade +/datum/fusionUpgradeTable/proc/rod_coef(obj/item/weapon/neutronRod/rod) + . = src.rod[rod.mineral] + +//Returns a color asosiated with a rod +/datum/fusionUpgradeTable/proc/rod_color(obj/item/weapon/neutronRod/rod) + . = src.rod_color[rod.mineral] + +//Field upgrade +/datum/fusionUpgradeTable/proc/field_coef(obj/item/weapon/shieldCrystal/crystal) + . = src.crystal[crystal.mineral] + +//Coefs on the fusion event determening heat, neutron, conversion rate, and fuel coefs +/datum/fusionUpgradeTable/proc/gas_coef(datum/gas_mixture/plasma) + //Gas propeties: + //Hydrogen - Basic fuel need at least 120 moles for 100% reactivity + //Phoron - Shield vitalizer, enhance shield regen rate + //Nitrogen - Stops reaction when more then 100 moles are present. dempense if less. + //Oxygen - Neutron releaser. + //CarbonDioxide - Heat absorver, neutron releaser. + //Nitrous Oxide - Neutron obsorber, heat releaser. + var/fuel_coef = Clamp(plasma.gas["hydrogen"]/(maxfuel/2), 0, 2) //120 moles are needed for full reactivity + var/dampening = 1 - Clamp(plasma.gas["nitrogen"]/100, 0, 1) + var/neutron_coef = 1 + Clamp(plasma.gas["oxygen"]/maxfuel, 0, 1) + var/heat_neutron_coef = plasma.gas["carbon_dioxide"]/maxfuel + var/neutron_heat_coef = plasma.gas["sleeping_agent"]/maxfuel + var/shield_coef = 1 + Clamp(plasma.gas["phoron"]/maxfuel, 0 ,1) + var/explosive = 0 + if(plasma.gas["phoron"] > 0 && plasma.gas["oxygen"] > 0) + explosive = 1 + + . = list(\ + "fuel" = fuel_coef,\ + "dampening" = dampening,\ + "neutron" = neutron_coef,\ + "shield" = shield_coef,\ + "heat_neutron" = heat_neutron_coef,\ + "neutron_heat" = neutron_heat_coef,\ + "explosive" = explosive\ + ) + + +//Upgrade items for the fusion reactor +//The rod has an effect on heat/neutron production. +/obj/item/weapon/neutronRod + name = "Neutron Absobtion Rod" + desc = " neutron absorbtion rod." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "smes_coil" // Just few icons patched together. If someone wants to make better icon, feel free to do so! + w_class = 4.0 // It's LARGE (backpack size) + var/mineral = "iron" + +/obj/item/weapon/neutronRod/New(var/mineral) + if(!isnull(mineral)) + src.mineral = mineral + ..() + +//The crystal has an effect on the decay/strengh of plasma/shields +/obj/item/weapon/shieldCrystal + name = "Field Amplification Crystal" + desc = " field amplification crystal." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "smes_coil" // Just few icons patched together. If someone wants to make better icon, feel free to do so! + w_class = 4.0 // It's LARGE (backpack size) + var/mineral = "glass" + +/obj/item/weapon/shieldCrystal/New(var/mineral) + if(!isnull(mineral)) + src.mineral = mineral + desc = "[mineral]" + desc + ..() \ No newline at end of file diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index c19d240731..dac024826c 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -334,7 +334,8 @@ //power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null //source is an object caused electrocuting (airlock, grille, etc) //No animations will be performed by this proc. -/proc/electrocute_mob(mob/living/carbon/M as mob, var/power_source, var/obj/source, var/max_damage = 25, var/siemens_coeff = 1.0) +//the arc = 0 var is here to make sure you DIE DIE DIE from an arc emitter/fusion event/fusion_ball +/proc/electrocute_mob(mob/living/carbon/M as mob, var/power_source, var/obj/source, var/max_damage = 25, var/siemens_coeff = 1.0, var/arc = 0) if(istype(M.loc,/obj/mecha)) return 0 //feckin mechs are dumb var/area/source_area if(istype(power_source,/area)) @@ -376,7 +377,7 @@ var/mob/living/carbon/human/H = M if(H.species.siemens_coefficient == 0) return - if(H.gloves) + if(H.gloves && !arc) var/obj/item/clothing/gloves/G = H.gloves if(G.siemens_coefficient == 0) return 0 //to avoid spamming with insulated glvoes on diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 24951af07c..b342bfecea 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -351,6 +351,22 @@ damage += Proj.damage return 0 +/obj/machinery/power/supermatter/proc/arc_act(energy) + var/turf/L = loc + if(!istype(L)) // We don't run process() when we are in space + return 0 // This stops people from being able to really power up the supermatter + // Then bring it inside to explode instantly upon landing on a valid turf. + var/factor = getSMVar( smlevel, "emitter_factor" ) + power += ( 0.16 * ( 1.69 ** factor )) * ( energy / ( ARC_EMITTER_POWER_MAX * 0.6667)) *50 // regression, times 50 to make up for arc emitter fire rate. + if(smlevel > 1) + //If above level 1 + //Dam calc with a exponential factor (simular as above). + //Dam vars can be adjusted in /datum/sm_control + var/dama = getSMVar( smlevel, "dama") + var/damb = getSMVar( smlevel, "damb") + var/damc = getSMVar( smlevel, "damc") + damage += ((dama*(damb ** factor)) * energy)/damc * 50 //Times 50 to make up for arc emitter fire rate. + /obj/machinery/power/supermatter/attack_robot(mob/user as mob) if(Adjacent(user)) return attack_hand(user) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 8a38a0325d..0d59f5c8c6 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -41,4 +41,4 @@ if(modifystate) icon_state = "[modifystate][ratio]" else - icon_state = "[initial(icon_state)][ratio]" + icon_state = "[initial(icon_state)][ratio]" \ No newline at end of file diff --git a/code/modules/research/alloy_synth.dm b/code/modules/research/alloy_synth.dm index 6e85d7da6a..639c98b3d5 100644 --- a/code/modules/research/alloy_synth.dm +++ b/code/modules/research/alloy_synth.dm @@ -139,19 +139,19 @@ Creates alloys that can be used to make stronger structures or more complex allo var/obj/item/stack/sheet/alloy/A = null var/obj/item/stack/sheet/mineral/M = mineral if(base.name == "metal") - A = new /obj/item/stack/sheet/alloy/metal(comp) + A = new /obj/item/stack/sheet/alloy/metal(comp, mineral.name, base.name) // >= 40% platinum is considered plasteel // can't do this in the alloy New() :( if(A.materials["platinum"] && A.materials["platinum"] >= 0.4) qdel(A) - A = new /obj/item/stack/sheet/alloy/plasteel(comp) + A = new /obj/item/stack/sheet/alloy/plasteel(comp, mineral.name, base.name) else //You can now make phoron glass with this ! if(mineral.name == "solid phoron" && base.amount/mineral.amount == 2) A = new /obj/item/stack/sheet/glass/phoronglass() else - A = new /obj/item/stack/sheet/alloy/glass(comp) + A = new /obj/item/stack/sheet/alloy/glass(comp, mineral.name, base.name) A.effects = M.mineral_effect A.amount = base.amount A.loc = get_turf(src) diff --git a/code/modules/research/xenoarchaeology/tools/anomaly_suit.dm b/code/modules/research/xenoarchaeology/tools/anomaly_suit.dm index 67ac1bd867..fa46e74868 100644 --- a/code/modules/research/xenoarchaeology/tools/anomaly_suit.dm +++ b/code/modules/research/xenoarchaeology/tools/anomaly_suit.dm @@ -6,6 +6,7 @@ icon_state = "engspace_suit" item_state = "engspace_suit" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100) + siemens_coefficient = 0.9 /obj/item/clothing/head/bio_hood/anomaly name = "Anomaly hood" @@ -13,6 +14,7 @@ icon_state = "engspace_helmet" item_state = "engspace_helmet" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100) + siemens_coefficient = 0.9 /obj/item/clothing/suit/space/anomaly name = "Excavation suit" @@ -21,6 +23,7 @@ item_state = "cespace_suit" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100) allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit) + siemens_coefficient = 0.9 /obj/item/clothing/head/helmet/space/anomaly name = "Excavation hood" @@ -28,3 +31,4 @@ icon_state = "cespace_helmet" item_state = "cespace_helmet" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100) + siemens_coefficient = 0.9 \ No newline at end of file diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index f42da1c469..4b04eda1e3 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/segment.dmi b/icons/effects/segment.dmi new file mode 100644 index 0000000000..c6d1110e54 Binary files /dev/null and b/icons/effects/segment.dmi differ diff --git a/icons/obj/atmos.dmi b/icons/obj/atmos.dmi index 938b574603..b03f65b639 100644 Binary files a/icons/obj/atmos.dmi and b/icons/obj/atmos.dmi differ diff --git a/icons/obj/fusion.dmi b/icons/obj/fusion.dmi new file mode 100644 index 0000000000..71c974800b Binary files /dev/null and b/icons/obj/fusion.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index 48a92ffe14..e5d16cb0b0 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/icons/obj/tank.dmi b/icons/obj/tank.dmi index 06a709f44b..2c1caed59a 100644 Binary files a/icons/obj/tank.dmi and b/icons/obj/tank.dmi differ