diff --git a/baystation12.dme b/baystation12.dme
index 2624afaac9e..d11073692a8 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -21,6 +21,7 @@
#include "code\__HELPERS\global_lists.dm"
#include "code\__HELPERS\icons.dm"
#include "code\__HELPERS\lists.dm"
+#include "code\__HELPERS\loc.dm"
#include "code\__HELPERS\logging.dm"
#include "code\__HELPERS\maths.dm"
#include "code\__HELPERS\mobs.dm"
@@ -30,6 +31,9 @@
#include "code\__HELPERS\time.dm"
#include "code\__HELPERS\type2type.dm"
#include "code\__HELPERS\unsorted.dm"
+#include "code\_hooks\area.dm"
+#include "code\_hooks\hooks.dm"
+#include "code\_hooks\mob.dm"
#include "code\_onclick\adjacent.dm"
#include "code\_onclick\ai.dm"
#include "code\_onclick\click.dm"
@@ -38,6 +42,7 @@
#include "code\_onclick\item_attack.dm"
#include "code\_onclick\observer.dm"
#include "code\_onclick\other_mobs.dm"
+#include "code\_onclick\overmind.dm"
#include "code\_onclick\telekinesis.dm"
#include "code\_onclick\hud\_defines.dm"
#include "code\_onclick\hud\alien.dm"
@@ -76,11 +81,9 @@
#include "code\ATMOSPHERICS\components\unary\unary_base.dm"
#include "code\ATMOSPHERICS\components\unary\vent_pump.dm"
#include "code\ATMOSPHERICS\components\unary\vent_scrubber.dm"
-#include "code\controllers\_DynamicAreaLighting_TG.dm"
#include "code\controllers\configuration.dm"
#include "code\controllers\failsafe.dm"
#include "code\controllers\garbage.dm"
-#include "code\controllers\lighting_controller.dm"
#include "code\controllers\master_controller.dm"
#include "code\controllers\shuttle_controller.dm"
#include "code\controllers\verbs.dm"
@@ -212,6 +215,7 @@
#include "code\game\dna\genes\goon_powers.dm"
#include "code\game\dna\genes\monkey.dm"
#include "code\game\dna\genes\powers.dm"
+#include "code\game\dna\genes\vg_powers.dm"
#include "code\game\gamemodes\events.dm"
#include "code\game\gamemodes\factions.dm"
#include "code\game\gamemodes\game_mode.dm"
@@ -274,6 +278,7 @@
#include "code\game\gamemodes\vox\heist\heist.dm"
#include "code\game\gamemodes\vox\trade\trade.dm"
#include "code\game\gamemodes\wizard\artefact.dm"
+#include "code\game\gamemodes\wizard\raginmages.dm"
#include "code\game\gamemodes\wizard\rightandwrong.dm"
#include "code\game\gamemodes\wizard\soulstone.dm"
#include "code\game\gamemodes\wizard\spellbook.dm"
@@ -337,6 +342,7 @@
#include "code\game\machinery\portable_turret.dm"
#include "code\game\machinery\recharger.dm"
#include "code\game\machinery\rechargestation.dm"
+#include "code\game\machinery\recycler.dm"
#include "code\game\machinery\requests_console.dm"
#include "code\game\machinery\robot_fabricator.dm"
#include "code\game\machinery\seed_extractor.dm"
@@ -396,6 +402,7 @@
#include "code\game\machinery\computer\prisoner.dm"
#include "code\game\machinery\computer\prisonshuttle.dm"
#include "code\game\machinery\computer\robot.dm"
+#include "code\game\machinery\computer\salvage_ship.dm"
#include "code\game\machinery\computer\security.dm"
#include "code\game\machinery\computer\shuttle.dm"
#include "code\game\machinery\computer\skills.dm"
@@ -454,6 +461,7 @@
#include "code\game\mecha\combat\honker.dm"
#include "code\game\mecha\combat\marauder.dm"
#include "code\game\mecha\combat\phazon.dm"
+#include "code\game\mecha\combat\recitence.dm"
#include "code\game\mecha\equipment\mecha_equipment.dm"
#include "code\game\mecha\equipment\tools\medical_tools.dm"
#include "code\game\mecha\equipment\tools\tools.dm"
@@ -918,11 +926,16 @@
#include "code\modules\maps\reader.dm"
#include "code\modules\maps\swapmaps.dm"
#include "code\modules\maps\writer.dm"
+#include "code\modules\media\jukebox.dm"
+#include "code\modules\media\machinery.dm"
+#include "code\modules\media\mediamanager.dm"
#include "code\modules\mining\abandonedcrates.dm"
+#include "code\modules\mining\equipment_locker.dm"
#include "code\modules\mining\machine_input_output_plates.dm"
#include "code\modules\mining\machine_processing.dm"
#include "code\modules\mining\machine_stacking.dm"
#include "code\modules\mining\machine_unloading.dm"
+#include "code\modules\mining\materials.dm"
#include "code\modules\mining\mine_areas.dm"
#include "code\modules\mining\mine_items.dm"
#include "code\modules\mining\mine_turfs.dm"
@@ -931,9 +944,12 @@
#include "code\modules\mining\money_bag.dm"
#include "code\modules\mining\ores_coins.dm"
#include "code\modules\mining\satchel_ore_boxdm.dm"
+#include "code\modules\mining\surprise.dm"
#include "code\modules\mining\laborcamp\laborminerals.dm"
#include "code\modules\mining\laborcamp\laborshuttle.dm"
#include "code\modules\mining\laborcamp\laborstacker.dm"
+#include "code\modules\mining\surprises\tg.dm"
+#include "code\modules\mining\surprises\vg.dm"
#include "code\modules\mob\abilities.dm"
#include "code\modules\mob\death.dm"
#include "code\modules\mob\emote.dm"
@@ -978,7 +994,6 @@
#include "code\modules\mob\living\carbon\alien\powers.dm"
#include "code\modules\mob\living\carbon\alien\say.dm"
#include "code\modules\mob\living\carbon\alien\humanoid\alien_powers.dm"
-#include "code\modules\mob\living\carbon\alien\humanoid\death.dm"
#include "code\modules\mob\living\carbon\alien\humanoid\emote.dm"
#include "code\modules\mob\living\carbon\alien\humanoid\empress.dm"
#include "code\modules\mob\living\carbon\alien\humanoid\humanoid.dm"
@@ -1119,6 +1134,7 @@
#include "code\modules\mob\living\simple_animal\hostile\mushroom.dm"
#include "code\modules\mob\living\simple_animal\hostile\pirate.dm"
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
+#include "code\modules\mob\living\simple_animal\hostile\statue.dm"
#include "code\modules\mob\living\simple_animal\hostile\syndicate.dm"
#include "code\modules\mob\living\simple_animal\hostile\tree.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
@@ -1382,6 +1398,12 @@
#include "code\WorkInProgress\fireworks.dm"
#include "code\WorkInProgress\periodic_news.dm"
#include "code\WorkInProgress\Apples\artifacts.dm"
+#include "code\WorkInProgress\Aryn\Lighting\_Defs.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Compatibility.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Controller.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Engine.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Light.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Lightpoint.dm"
#include "code\WorkInProgress\Cael_Aislinn\sculpture.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\falsewall.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\jungle.dm"
@@ -1448,6 +1470,8 @@
#include "code\WorkInProgress\computer3\computers\robot.dm"
#include "code\WorkInProgress\kilakk\fax.dm"
#include "code\WorkInProgress\Mini\atmos_control.dm"
+#include "code\WorkInProgress\pomf\spacepods\equipment.dm"
+#include "code\WorkInProgress\pomf\spacepods\spacepods.dm"
#include "code\WorkInProgress\Ported\policetape.dm"
#include "code\WorkInProgress\Sayu\belt.dm"
#include "code\WorkInProgress\Sayu\cargoprofile.dm"
@@ -1464,17 +1488,21 @@
#include "code\WorkInProgress\ZomgPonies\oldcode\turntable.dm"
#include "code\WorkInProgress\ZomgPonies\powerarmor\powerarmor.dm"
#include "code\WorkInProgress\ZomgPonies\powerarmor\powerarmorcomponents.dm"
+#include "code\ZAS\_docs.dm"
+#include "code\ZAS\_gas_mixture.dm"
#include "code\ZAS\Airflow.dm"
+#include "code\ZAS\Atom.dm"
#include "code\ZAS\Connection.dm"
+#include "code\ZAS\ConnectionGroup.dm"
+#include "code\ZAS\ConnectionManager.dm"
+#include "code\ZAS\Controller.dm"
#include "code\ZAS\Debug.dm"
-#include "code\ZAS\FEA_gas_mixture.dm"
-#include "code\ZAS\FEA_system.dm"
+#include "code\ZAS\Diagnostic.dm"
#include "code\ZAS\Fire.dm"
-#include "code\ZAS\Functions.dm"
#include "code\ZAS\Plasma.dm"
+#include "code\ZAS\Turf.dm"
#include "code\ZAS\Variable Settings.dm"
-#include "code\ZAS\ZAS_Turfs.dm"
-#include "code\ZAS\ZAS_Zones.dm"
+#include "code\ZAS\Zone.dm"
#include "interface\interface.dm"
#include "interface\skin.dmf"
#include "maps\tgstation-redux-WIP.dmm"
diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
index 9d2a8e32e8a..bba620b8cb8 100644
--- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
@@ -44,7 +44,7 @@ obj/machinery/atmospherics/binary
return null
- Del()
+ Destroy()
loc = null
if(node1)
diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm
index 4b54a943773..13464ab4637 100644
--- a/code/ATMOSPHERICS/components/portables_connector.dm
+++ b/code/ATMOSPHERICS/components/portables_connector.dm
@@ -62,7 +62,7 @@
return null
- Del()
+ Destroy()
loc = null
if(connected_device)
diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
index d3f1116a430..6bbe4152b51 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
@@ -52,7 +52,7 @@ obj/machinery/atmospherics/trinary
return null
- Del()
+ Destroy()
loc = null
if(node1)
diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index 6e92ded3d6d..81a7577ce2e 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -78,7 +78,7 @@ obj/machinery/atmospherics/tvalve
return null
- Del()
+ Destroy()
loc = null
if(node1)
diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm
index b755071487e..cd6f9247ce2 100644
--- a/code/ATMOSPHERICS/components/unary/unary_base.dm
+++ b/code/ATMOSPHERICS/components/unary/unary_base.dm
@@ -28,7 +28,7 @@
return null
- Del()
+ Destroy()
loc = null
if(node)
diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm
index 2573366b222..0b201446c8f 100644
--- a/code/ATMOSPHERICS/components/unary/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm
@@ -104,7 +104,7 @@
if(pressure_checks&2)
pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound))
- if(pressure_delta > 0)
+ if(pressure_delta > 0.5)
if(air_contents.temperature > 0)
var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
@@ -122,7 +122,7 @@
if(pressure_checks&2)
pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure()))
- if(pressure_delta > 0)
+ if(pressure_delta > 0.5)
if(environment.temperature > 0)
var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
@@ -334,7 +334,7 @@
new /obj/item/pipe(loc, make_from=src)
del(src)
-/obj/machinery/atmospherics/unary/vent_pump/Del()
+/obj/machinery/atmospherics/unary/vent_pump/Destroy()
if(initial_loc)
initial_loc.air_vent_info -= id_tag
initial_loc.air_vent_names -= id_tag
diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
index 917d165a125..b812b9f9eca 100644
--- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
@@ -291,7 +291,7 @@
new /obj/item/pipe(loc, make_from=src)
del(src)
-/obj/machinery/atmospherics/unary/vent_scrubber/Del()
+/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
if(initial_loc)
initial_loc.air_scrub_info -= id_tag
initial_loc.air_scrub_names -= id_tag
diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm
index 24ddcde9075..8f770b75785 100644
--- a/code/ATMOSPHERICS/components/valve.dm
+++ b/code/ATMOSPHERICS/components/valve.dm
@@ -62,7 +62,7 @@ obj/machinery/atmospherics/valve
return null
- Del()
+ Destroy()
loc = null
if(node1)
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index e6736b466f5..ebd35f470bc 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -49,7 +49,7 @@ obj/machinery/atmospherics/pipe
return parent.return_network(reference)
- Del()
+ Destroy()
del(parent)
if(air_temporary)
loc.assume_air(air_temporary)
@@ -172,7 +172,7 @@ obj/machinery/atmospherics/pipe
else if(dir==12)
dir = 4
- Del()
+ Destroy()
if(node1)
node1.disconnect(src)
if(node2)
@@ -460,7 +460,7 @@ obj/machinery/atmospherics/pipe
..()
- Del()
+ Destroy()
if(node1)
node1.disconnect(src)
@@ -569,7 +569,7 @@ obj/machinery/atmospherics/pipe
else if (nodealert)
nodealert = 0
*/
- Del()
+ Destroy()
if(node1)
node1.disconnect(src)
@@ -680,7 +680,7 @@ obj/machinery/atmospherics/pipe
else if (nodealert)
nodealert = 0
*/
- Del()
+ Destroy()
if(node1)
node1.disconnect(src)
if(node2)
@@ -933,7 +933,7 @@ obj/machinery/atmospherics/pipe
else if (nodealert)
nodealert = 0
*/
- Del()
+ Destroy()
if(node1)
node1.disconnect(src)
if(node2)
@@ -1133,7 +1133,7 @@ obj/machinery/atmospherics/pipe
..()
else
. = PROCESS_KILL
- Del()
+ Destroy()
if(node)
node.disconnect(src)
diff --git a/code/WorkInProgress/Aryn/Lighting/Compatibility.dm b/code/WorkInProgress/Aryn/Lighting/Compatibility.dm
new file mode 100644
index 00000000000..ad5ca37b047
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Compatibility.dm
@@ -0,0 +1,49 @@
+area/var/lighting_use_dynamic
+
+turf/space/is_outside = 1
+turf/simulated/shuttle/is_outside = 1
+
+/datum/controller/lighting/var/processing = 1
+/datum/controller/lighting/var/iteration = 0
+
+//Because so many objects jump the gun.
+proc/lighting_ready()
+ return lighting_controller && lighting_controller.started
+
+turf_light_data
+ var/light_overlay
+ var/lightNW
+ var/lightSW
+ var/lightNE
+ var/lightSE
+ var/lit_by
+
+turf_light_data/proc/copy_from(turf/T)
+ light_overlay = T.light_overlay
+ lightNW = T.lightNW
+ lightSW = T.lightSW
+ lightNE = T.lightNE
+ lightSE = T.lightSE
+ lit_by = T.lit_by
+
+turf_light_data/proc/copy_to(turf/T)
+ T.light_overlay = light_overlay
+ T.lightNW = lightNW
+ T.lightSW = lightSW
+ T.lightNE = lightNE
+ T.lightSE = lightSE
+ T.lit_by = lit_by
+ //T.ResetValue()
+
+atom/proc/SetLuminosity(n)
+ ASSERT(n >= 0)
+
+ n = min(n,10) //Caelcode.
+ if(n > 0)
+ //world << "[name].SetLuminosity([n]) \[[max(1,n>>1)],[n]\]"
+ SetLight(max(1,n),n)
+ else
+ //world << "[name].SetLuminosity(0)"
+ SetLight(0,0)
+ luminosity = n
+ //else lighting_controller.initial_lights.Add(src)
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Controller.dm b/code/WorkInProgress/Aryn/Lighting/Controller.dm
new file mode 100644
index 00000000000..6220ebe3609
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Controller.dm
@@ -0,0 +1,99 @@
+/*
+
+Overview:
+ Unlike the previous lighting controller, this is mostly here to hold global lighting procs and vars.
+ It does not process every tick, because not even DAL did something so stupid.
+
+Global Vars:
+ initial_lights - This holds all lights formed before the lighting controller started up. It becomes null on initialization.
+
+Class Vars:
+ starlight - The light value of space.
+
+ icon_updates - The list of turfs which need an update to their overlays.
+
+ light_border - Space turfs which are adjacent to non-space turfs.
+
+Class Procs:
+ Initialize()
+ Starts the lighting system, creating all light points and turf overlays.
+
+ StarLight(n)
+ Sets the light produced by space. If a solar eclipse suddenly happens, it'll probably lag.
+
+ MarkIconUpdate(turf/T)
+ Called when a turf needs an update to its light icon. Ensures that it only gets calculated once per turf.
+
+ FlushIconUpdates()
+ Called when a light is done marking icon updates. Updates every marked turf.
+
+ AddBorder(turf/T) & RemoveBorder(turf/T)
+ Called by turf/CheckForOpaqueObjects() to modify the light_border list.
+
+
+*/
+
+var/datum/controller/lighting/lighting_controller
+
+var/all_lightpoints_made = 0
+
+var/list/lit_z_levels = list(1,5)
+
+/datum/controller/lighting
+
+ var/starlight = LIGHT_STATES
+ var/list/icon_updates = list()
+ var/started = 0
+
+ //var/icon/border = icon('Icons/Test.dmi', "border")
+
+/datum/controller/lighting/New()
+ lighting_controller = src
+
+/datum/controller/lighting/proc/Initialize()
+
+ set background = 1
+
+ var/start_time = world.timeofday
+ world << "Processing lights..."
+
+ sleep(1)
+
+ var/turfs_updated = 0
+ var/total_turfs = world.maxx*world.maxy*lit_z_levels.len
+
+ for(var/z in lit_z_levels)
+ for(var/y = 0, y <= world.maxy, y++)
+ for(var/x = 0, x <= world.maxx, x++)
+ if(x > 0 && y > 0)
+
+ turfs_updated++
+ if((turfs_updated % (total_turfs>>2)) == 0)
+ sleep(1)
+ world << "Progress: [round((turfs_updated/total_turfs)*100, 0.01)]% ([turfs_updated]/[total_turfs])"
+
+ var/turf/T = locate(x,y,z)
+ if(!T.is_outside)
+ T.light_overlay = new(T)
+ //T.ResetValue()
+ if(!all_lightpoints_made) new/lightpoint(x+0.5,y+0.5,z)
+
+
+
+ //world << "[x],[y],[z]"
+
+ all_lightpoints_made = 1
+ started = 1
+
+ for(var/turf/T)
+ if(T.light_overlay)
+
+ T.ResetValue()
+ T.light_overlay.set_state(MAX_VALUE(T.lightSE), MAX_VALUE(T.lightSW), MAX_VALUE(T.lightNW), MAX_VALUE(T.lightNE))
+
+ world << "Lighting initialization took [(world.timeofday-start_time)/world.fps] seconds."
+ world << "Updated [turfs_updated] turfs."
+
+/datum/controller/lighting/proc/GetLightIcon(a,b,c,d)
+ if(a <= 2) return LIGHT_ICON_1
+ else return LIGHT_ICON_2
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Engine.dm b/code/WorkInProgress/Aryn/Lighting/Engine.dm
new file mode 100644
index 00000000000..fc94b09c6a4
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Engine.dm
@@ -0,0 +1,231 @@
+/*
+
+Overview:
+ Procs given to atom and turf by the lighting engine, as well as the lighting overlay object.
+
+Atom Vars:
+ light - Contains the light object this atom is currently shining with.
+
+Turf Vars:
+ light_overlay - Contains an object showing the lighting icon over this turf.
+
+ lit_value - Stores how brightly lit the turf is.
+
+ has_opaque - A cached value updated by CheckForOpaqueObjects()
+
+ is_outside - Any turf with this set to true will be considered as bright as space.
+
+ needs_light_update - Turf is marked for icon updates when true.
+
+ lightNE, lightSE, lightNW, lightSW - Hold the lightpoints on the four corners of this turf. See Lightpoint.dm
+
+ lit_by - A list of lights that are lighting this turf.
+
+Atom Procs:
+ SetLight(intensity, radius)
+ A more versatile SetLuminosity() that allows independent control of intensity and radius.
+ Called behind the scenes of SetLuminosity().
+
+ SetOpacity(opacity)
+ Does the same thing as DAL.
+
+Turf Procs:
+ UpdateLight()
+ Called by the lighting controller. It is advisable not to call this manually due to the cost of lightpoint/max_value()
+
+ AddLight(light/light)
+ Called by light/Reset() to light this turf with a particular light.
+
+ RemoveLight(light/light)
+ Called by light/Off() to unlight turfs that were lit by it.
+
+ ResetValue()
+ Called when lights are reset or starlight is changed.
+
+ ResetCachedValues()
+ Resets cached values of all four light points. Called by ResetValue().
+
+ CheckForOpaqueObjects()
+ Called by lighting_controller.Initialize(), SetOpacity() or when a turf might change opacity.
+ Resets the opacity cache and looks for opaque objects. Also responsible for adding and removing borders to space.
+*/
+
+atom/movable/lighting_overlay
+ name = ""
+ anchored = 1
+ layer = 9
+ mouse_opacity = 0
+ invisibility = INVISIBILITY_LIGHTING
+
+atom/movable/lighting_overlay/proc/set_state(a,b,c,d)
+ icon_state = "[a][b][c][d]"
+ icon = lighting_controller.GetLightIcon(a,b,c,d)
+
+atom/var/light/light
+
+turf/var/atom/movable/lighting_overlay/light_overlay
+
+turf/var/lit_value = 0
+turf/var/max_brightness = 0
+turf/var/has_opaque = -1
+turf/var/is_outside = 0
+turf/var/is_border = 0
+
+turf/var/lightpoint/lightNE
+turf/var/lightpoint/lightNW
+turf/var/lightpoint/lightSE
+turf/var/lightpoint/lightSW
+turf/var/list/lit_by
+
+atom/movable/New()
+ . = ..()
+ if(luminosity)
+ if(!light)
+ SetLight(luminosity,luminosity)
+ else
+ light.atom = src
+ light.Reset()
+ else if(light)
+ light.atom = src
+ light.Reset()
+ if(opacity)
+ if(lighting_ready())
+ opacity = 0
+ SetOpacity(1)
+
+atom/movable/Del()
+ if(light) light.Off()
+ if(opacity) SetOpacity(0)
+ . = ..()
+
+atom/movable/Move(turf/newloc)
+ var/o = opacity
+ if(o) SetOpacity(0)
+ . = ..()
+ if(.)
+ if(o) SetOpacity(1)
+
+turf/Entered(atom/movable/M)
+ . = ..()
+ if(M.light) M.light.Reset()
+
+atom/proc/SetLight(intensity, radius)
+ //if(lights_verbose) world << "SetLight([intensity],[radius])"
+ if(!intensity)
+ if(!light || !light.intensity)
+ //if(lights_verbose) world << "Still off."
+ return
+ //if(lights_verbose) world << "Shut off light with [light.lit_turfs.len] turfs lit."
+ light.Off()
+ light.intensity = 0
+ //if(lighting_ready()) lighting_controller.FlushIconUpdates()
+ return
+ if(!light)
+ //if(lights_verbose) world << "New light."
+ light = new(src)
+ if(light.intensity == intensity)
+ //if(lights_verbose) world << "Same intensity."
+ return
+ light.radius = min(radius,15)
+ light.intensity = intensity
+ light.Reset()
+ //if(lighting_ready()) lighting_controller.FlushIconUpdates()
+
+atom/proc/SetOpacity(o)
+ if(o == opacity) return
+ opacity = o
+ var/turf/T = loc
+ if(isturf(T))
+ T.CheckForOpaqueObjects()
+ for(var/light/A in T.lit_by)
+ A.Reset()
+
+ //lighting_controller.FlushIconUpdates()
+
+atom/proc/UpdateLights()
+ if(light) light.Reset()
+ for(var/atom/movable/A in src)
+ if(A.light) A.light.Reset()
+
+//turf/proc/UpdateLight()
+// if(light_overlay)
+// light_overlay.icon_state = "[lightSE.max_value()][lightSW.max_value()][lightNW.max_value()][lightNE.max_value()]"
+
+turf/proc/AddLight(light/light, brightness)
+ if(is_outside) return
+
+ if(brightness <= 0) return
+
+ if(!lit_by) lit_by = list()
+ lit_by.Add(light)
+
+
+ lit_by[light] = brightness
+
+ if(lighting_ready())
+ if(brightness > max_brightness)
+ lit_value = LIGHTCLAMP(brightness)
+ max_brightness = brightness
+
+ if(lightNE) lightNE.cached_value = -1
+ if(lightNW) lightNW.cached_value = -1
+ if(lightSE) lightSE.cached_value = -1
+ if(lightSW) lightSW.cached_value = -1
+
+ for(var/turf/T in range(1,src))
+ if(T.light_overlay)
+ T.light_overlay.set_state(MAX_VALUE(T.lightSE), MAX_VALUE(T.lightSW), MAX_VALUE(T.lightNW), MAX_VALUE(T.lightNE))
+
+turf/proc/RemoveLight(light/light)
+ if(lit_by)
+ var/brightness = lit_by[light]
+ lit_by.Remove(light)
+ if(brightness == max_brightness)
+ ResetValue()
+ if(!lit_by.len) lit_by = null
+
+//Only called by ChangeTurf, because it really needs it.
+turf/proc/ResetAllLights()
+ for(var/light/light in lit_by)
+ light.Reset()
+
+/turf/space/ResetAllLights()
+ var/atom/movable/lighting_overlay/overlay = locate() in src
+ if(overlay) overlay.loc = null
+ light_overlay = null
+ is_outside = 1
+ . = ..()
+
+turf/proc/ResetValue()
+ if(is_outside)
+ max_brightness = lighting_controller.starlight
+ lit_value = LIGHTCLAMP(lighting_controller.starlight)
+ return
+
+ if(has_opaque < 0) CheckForOpaqueObjects()
+ if(has_opaque)
+ lit_value = 0
+ else
+ max_brightness = 0
+ for(var/light/light in lit_by)
+ var/brightness = lit_by[light]//light.CalculateBrightness(src)
+ if(brightness > max_brightness)
+ max_brightness = brightness
+ lit_value = LIGHTCLAMP(max_brightness)
+
+ if(lighting_ready())
+ if(lightNE) lightNE.cached_value = -1
+ if(lightNW) lightNW.cached_value = -1
+ if(lightSE) lightSE.cached_value = -1
+ if(lightSW) lightSW.cached_value = -1
+ for(var/turf/T in range(1,src))
+ if(T.light_overlay)
+ T.light_overlay.set_state(MAX_VALUE(T.lightSE), MAX_VALUE(T.lightSW), MAX_VALUE(T.lightNW), MAX_VALUE(T.lightNE))
+
+turf/proc/CheckForOpaqueObjects()
+ has_opaque = opacity
+ if(!opacity)
+ for(var/atom/movable/M in contents)
+ if(M.opacity)
+ has_opaque = 1
+ break
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Light.dm b/code/WorkInProgress/Aryn/Lighting/Light.dm
new file mode 100644
index 00000000000..fd6f196645b
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Light.dm
@@ -0,0 +1,89 @@
+/*
+
+Overview:
+ This object functions similarly to /tg/'s /light. It is responsible for calculating what turfs are lit by it.
+
+Class Vars:
+ radius - Set by atom/SetLight(). This stores how far out turfs will be lit up.
+ intensity - Set by atom/SetLight(). Stores the amount of light generated at the center.
+ lit_turfs - A list of turfs being lit by this light.
+ atom - The atom this light is attached to.
+
+Class Procs:
+ Reset()
+ This is called whenever the light changes, or the underlying atom changes position.
+
+ Off()
+ A quick way to turn off a light. Removes the light from all turfs in lit_turfs.
+
+ CalculateBrightness(turf/T)
+ Returns the brightness that should be displayed by this light on a specific turf.
+
+*/
+
+
+light/var/radius = 0
+light/var/intensity = 0
+light/var/ambient_extension = 3
+light/var/list/lit_turfs = list()
+light/var/atom/atom
+
+light/New(atom/atom, radius, ambience=3)
+ ASSERT(atom)
+
+ if(istype(atom))
+ src.atom = atom
+ else
+ src.intensity = atom
+ src.radius = radius
+ src.ambient_extension = ambience
+
+light/proc/Reset()
+ //if(atom.lights_verbose) world << "light.Reset()"
+ Off()
+ if(intensity > 0)
+ //if(atom.lights_verbose) world << "Restoring light."
+ var/turf/loc = atom
+ for(var/turf/T in view(loc,radius+ambient_extension))
+ if(!T.is_outside)
+ var/brightness = CalculateBrightness(T, loc)
+ T.AddLight(src, brightness)
+ lit_turfs.Add(T)
+ //if(atom.lights_verbose) world << "[lit_turfs.len] turfs added."
+
+light/proc/Off()
+ //if(atom.lights_verbose) world << "light.Off()"
+ for(var/turf/T in lit_turfs)
+ T.RemoveLight(src)
+ lit_turfs.Cut()
+
+light/proc/Flash(t)
+ Reset()
+ spawn(t)
+ Off()
+
+light/proc/CalculateBrightness(turf/T, turf/loc)
+ ASSERT(T)
+ var/square = DISTSQ3(loc.x-T.x,loc.y-T.y,loc.z-T.z)
+ if(square > (radius+ambient_extension)*(radius+ambient_extension)) return 0
+ //+2 offset gives an ambient light effect.
+
+ var/value = ((radius)/(2*FSQRT(square) + 1)) * intensity - 0.48
+ /*
+ lightRadius
+ ---------------- * lightValue - 0.48
+ 2 * distance + 1
+
+ The light decreases by twice the distance, starting from the radius.
+ The + 1 causes the graph to shift to the left one unit so that division by zero is prevented on the source tile.
+
+ This is then multiplied by the light value to give the final result.
+ The -0.48 offset causes the value to be near zero at the radius.
+
+ This gives a result which is likely close to the inverse-square law in two dimensions instead of three.
+ */
+
+
+ return max(min( value , intensity), 0) //Ensure the value never goes above the maximum light value or below zero.
+
+ //return cos(90 * sqrt(square) / max(1,lightRadius)) * lightValue
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm b/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm
new file mode 100644
index 00000000000..eeeec4c7068
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm
@@ -0,0 +1,59 @@
+/*
+
+Overview:
+ Perhaps the most cryptic datum in the lighting engine, there are four of these at the corners of every turf.
+ Any two turfs that share a corner will also have the same lightpoint. Because of the nature of the icons used,
+ light is shown at the corner of the turf rather than in the middle, necessitating some way to keep track of what
+ icon state to use.
+
+Class Vars:
+ x, y, z - The position of the lightpoint. x and y will usually be expressed in terms of 0.5 due to its location on the corner.
+
+ NE, NW, SE, SW - The turfs that are in these directions relative to the lightpoint.
+
+ cached_value - A cached value of max_value().
+
+Class Procs:
+ max_value()
+ The maximum of the light amounts on the four turfs of this light point.
+
+*/
+
+lightpoint
+ var/x
+ var/y
+ var/z
+
+ var/turf/NE
+ var/turf/NW
+ var/turf/SW
+ var/turf/SE
+
+ var/cached_value = -1
+
+ New(x,y,z)
+ var/turf/T = locate(x+0.5,y+0.5,z)
+ if(T)
+ NE = T
+ T.lightSW = src
+ T = locate(x-0.5,y+0.5,z)
+ if(T)
+ NW = T
+ T.lightSE = src
+ T = locate(x-0.5,y-0.5,z)
+ if(T)
+ SW = T
+ T.lightNE = src
+ T = locate(x+0.5,y-0.5,z)
+ if(T)
+ SE = T
+ T.lightNW = src
+
+ proc/max_value()
+ var
+ valueA = VALUE_OF(NW)
+ valueB = VALUE_OF(NE)
+ valueC = VALUE_OF(SW)
+ valueD = VALUE_OF(SE)
+ cached_value = max(valueA,valueB,valueC,valueD)
+ return cached_value
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Verbs.dm b/code/WorkInProgress/Aryn/Lighting/Verbs.dm
new file mode 100644
index 00000000000..f9e4d7c688f
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Verbs.dm
@@ -0,0 +1,34 @@
+var/icon/lighting_dbg = icon('icons/Testing/Zone.dmi', "created")
+
+atom/var/lights_verbose = 0
+
+obj/machinery/light/verb/ShowInfluence()
+ set src in world
+
+ lights_verbose = 1
+ usr << "[src]"
+ if(light)
+ usr << "Intensity: [light.intensity]"
+ usr << "Radius: [light.radius]"
+
+ for(var/turf/T in light.lit_turfs)
+ T.overlays += lighting_dbg
+ spawn(50)
+ for(var/turf/T in light.lit_turfs)
+ T.overlays -= lighting_dbg
+
+turf/verb/ShowData()
+ set src in world
+
+ usr << "[src]"
+ usr << "[MAX_VALUE(lightSE)][MAX_VALUE(lightSW)][MAX_VALUE(lightNW)][MAX_VALUE(lightNE)]"
+ usr << "Lit Value: [lit_value]"
+ usr << "Max Brightness: [max_brightness]"
+
+ usr << "Lit By: "
+ for(var/light/light in lit_by)
+ usr << " - [light.atom] \[[lit_by[light]]\][(lit_by[light] == max_brightness ? "(MAX)" : "")]"
+ light.atom.overlays += lighting_dbg
+ spawn(50)
+ for(var/light/light in lit_by)
+ light.atom.overlays -= lighting_dbg
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/_Defs.dm b/code/WorkInProgress/Aryn/Lighting/_Defs.dm
new file mode 100644
index 00000000000..72069a1afa8
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/_Defs.dm
@@ -0,0 +1,19 @@
+#define LIGHT_ICON_1 'icons/effects/lights/lighting5-1.dmi'
+#define LIGHT_ICON_2 'icons/effects/lights/lighting5-2.dmi'
+#define LIGHT_STATES 4
+
+#define SQ(X) ((X)*(X))
+#define DISTSQ3(A,B,C) (SQ(A)+SQ(B)+SQ(C))
+#define FSQRT(X) (X >= fastroot.len ? new_fsqrt(X) : fastroot[(X)+1])
+#define MAX_VALUE(X) (X.cached_value < 0 ? X.max_value() : X.cached_value)
+#define VALUE_OF(X) ( !X ? 0 : ( X.is_outside ? LIGHTCLAMP(lighting_controller.starlight) : X.lit_value ) )
+#define LIGHTCLAMP(x) ( max(0,min(LIGHT_STATES,round(x,1))) )
+
+var/list/fastroot = list(0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7)
+
+proc/new_fsqrt(n)
+ //world << "Adding [n-fastroot.len] entries to root table."
+ for(var/i = fastroot.len, i <= n, i++)
+ fastroot += round(sqrt(i))
\ No newline at end of file
diff --git a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_animals.dm b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_animals.dm
index be4d60fe6a5..79dfb3fb356 100644
--- a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_animals.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_animals.dm
@@ -23,7 +23,7 @@
spawn(rand(1200,2400))
spawned_animal.loc = locate(src.x + rand(-12,12), src.y + rand(-12,12), src.z)
-/obj/effect/landmark/animal_spawner/Del()
+/obj/effect/landmark/animal_spawner/Destroy()
processing_objects.Remove(src)
/obj/effect/landmark/animal_spawner/panther
diff --git a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_tribe.dm b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_tribe.dm
index 46ad23cedff..2747ce063c1 100644
--- a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_tribe.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_tribe.dm
@@ -27,7 +27,7 @@
T.y += rand(-6,6)
tribesmen += T
-/obj/effect/jungle_tribe_spawn/Del()
+/obj/effect/jungle_tribe_spawn/Destroy()
processing_objects.Remove(src)
/obj/effect/jungle_tribe_spawn/process()
diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm
index 09ac351d907..e42eedac03f 100644
--- a/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm
@@ -428,7 +428,7 @@ Deuterium-tritium fusion: 4.5 x 10^7 K
AddParticles(reactant, reactants_reacting_pool[reactant])
//world << "retained: [reactant], [reactants_reacting_pool[reactant]]"
-/obj/effect/rust_em_field/Del()
+/obj/effect/rust_em_field/Destroy()
//radiate everything in one giant burst
for(var/obj/effect/rust_particle_catcher/catcher in particle_catchers)
del (catcher)
diff --git a/code/WorkInProgress/Cael_Aislinn/Supermatter/Laser2.dm b/code/WorkInProgress/Cael_Aislinn/Supermatter/Laser2.dm
index 4a7779c3652..eeab2fafc76 100644
--- a/code/WorkInProgress/Cael_Aislinn/Supermatter/Laser2.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Supermatter/Laser2.dm
@@ -120,7 +120,7 @@
return
return
-/obj/beam/e_beam/Del()
+/obj/beam/e_beam/Destroy()
if(next)
del(next)
..()
diff --git a/code/WorkInProgress/Mini/atmos_control.dm b/code/WorkInProgress/Mini/atmos_control.dm
index 3eed620ee9d..275b038277e 100644
--- a/code/WorkInProgress/Mini/atmos_control.dm
+++ b/code/WorkInProgress/Mini/atmos_control.dm
@@ -58,7 +58,10 @@
data["alarm"] = "\ref[current]"
var/list/alarms=list()
- for(var/obj/machinery/alarm/alarm in sortAtom(machines))
+ var/list/alarm_list=list()
+ for(var/obj/machinery/alarm/alarm in machines)
+ alarm_list+=alarm
+ for(var/obj/machinery/alarm/alarm in sortAtom(alarm_list))
if(!is_in_filter(alarm.alarm_area.type))
continue // NO ACCESS 4 U
diff --git a/code/WorkInProgress/ZomgPonies/mobs/pony.dm b/code/WorkInProgress/ZomgPonies/mobs/pony.dm
index c57f841a63f..c9c9326c292 100644
--- a/code/WorkInProgress/ZomgPonies/mobs/pony.dm
+++ b/code/WorkInProgress/ZomgPonies/mobs/pony.dm
@@ -87,3 +87,63 @@ mob/living/simple_animal/pony/applejack
icon_state = "applejack"
icon_living = "applejack"
+mob/living/simple_animal/pony/luna
+ name = "Luna"
+ real_name = "Luna"
+ icon_state = "luna"
+ icon_living = "luna"
+
+mob/living/simple_animal/pony/clownie
+ name = "Clownie"
+ real_name = "Clownie"
+ icon_state = "clownie"
+ icon_living = "clownie"
+
+mob/living/simple_animal/pony/tia
+ name = "Tia"
+ real_name = "Tia"
+ icon_state = "tia"
+ icon_living = "tia"
+
+mob/living/simple_animal/pony/trixie
+ name = "Trixie"
+ real_name = "Trixie"
+ icon_state = "trixie_a_full"
+ icon_living = "trixing_a_full"
+
+mob/living/simple_animal/pony/lyra
+ name = "Lyra"
+ real_name = "Lyra"
+ icon_state = "lyra"
+ icon_living = "lyra"
+
+mob/living/simple_animal/pony/vinyl
+ name = "Vinyl"
+ real_name = "Vinyl"
+ icon_state = "vinyl"
+ icon_living = "vinyl"
+
+mob/living/simple_animal/pony/rarity
+ name = "Rarity"
+ real_name = "Rarity"
+ icon_state = "rarity"
+ icon_living = "rarity"
+
+mob/living/simple_animal/pony/whooves
+ name = "Whooves"
+ real_name = "Whooves"
+ icon_state = "whooves"
+ icon_living = "whooves"
+
+mob/living/simple_animal/pony/fleur
+ name = "Fleur"
+ real_name = "Fleur"
+ icon_state = "fleur"
+ icon_living = "fleur"
+
+mob/living/simple_animal/pony/mac
+ name = "Mac"
+ real_name = "Mac"
+ icon_state = "mac"
+ icon_living = "mac"
+
diff --git a/code/WorkInProgress/computer3/computers/communications.dm b/code/WorkInProgress/computer3/computers/communications.dm
index be433c7e757..01351dc4863 100644
--- a/code/WorkInProgress/computer3/computers/communications.dm
+++ b/code/WorkInProgress/computer3/computers/communications.dm
@@ -131,12 +131,6 @@
state = STATE_DEFAULT
if(authenticated)
state = STATE_CANCELSHUTTLE
- if("cancelshuttle2" in href_list)
- if(!computer.radio.subspace)
- return
- if(authenticated)
- cancel_call_proc(usr)
- state = STATE_DEFAULT
if("messagelist" in href_list)
currmsg = 0
state = STATE_MESSAGELIST
diff --git a/code/WorkInProgress/computer3/laptop.dm b/code/WorkInProgress/computer3/laptop.dm
index 7efc10fc81b..9c3d3a9dc75 100644
--- a/code/WorkInProgress/computer3/laptop.dm
+++ b/code/WorkInProgress/computer3/laptop.dm
@@ -122,7 +122,7 @@
else
stat &= ~NOPOWER
- Del()
+ Destroy()
if(istype(loc,/obj/item/device/laptop))
var/obj/O = loc
spawn(5)
diff --git a/code/WorkInProgress/pomf/spacepods/equipment.dm b/code/WorkInProgress/pomf/spacepods/equipment.dm
new file mode 100644
index 00000000000..2c3a673c703
--- /dev/null
+++ b/code/WorkInProgress/pomf/spacepods/equipment.dm
@@ -0,0 +1,50 @@
+
+/datum/spacepod/equipment
+ var/obj/spacepod/my_atom
+ var/obj/item/device/spacepod_equipment/weaponry/weapon_system // weapons system
+ //var/obj/item/device/spacepod_equipment/engine/engine_system // engine system
+ //var/obj/item/device/spacepod_equipment/shield/shield_system // shielding system
+
+/datum/spacepod/equipment/New(var/obj/spacepod/SP)
+ ..()
+ if(istype(SP))
+ my_atom = SP
+
+/obj/item/device/spacepod_equipment
+ name = "equipment"
+// base item for spacepod weapons
+
+/obj/item/device/spacepod_equipment/weaponry
+ name = "pod weapon"
+ desc = "You shouldn't be seeing this"
+ icon = 'icons/pods/ship.dmi'
+ icon_state = "blank"
+ var/projectile_type
+ var/shot_cost = 0
+ var/shots_per = 1
+ var/fire_sound
+ var/fire_delay = 10
+
+/obj/item/device/spacepod_equipment/weaponry/taser
+ name = "\improper taser system"
+ desc = "A weak taser system for space pods, fires electrodes that shock upon impact."
+ icon_state = "pod_taser"
+ projectile_type = "/obj/item/projectile/energy/electrode"
+ shot_cost = 10
+ fire_sound = "sound/weapons/Taser.ogg"
+
+/obj/item/device/spacepod_equipment/weaponry/taser/burst
+ name = "\improper taser system"
+ desc = "A weak taser system for space pods, this one fires 3 at a time."
+ icon_state = "pod_b_taser"
+ shot_cost = 20
+ shots_per = 3
+
+/obj/item/device/spacepod_equipment/weaponry/laser
+ name = "\improper laser system"
+ desc = "A weak laser system for space pods, fires concentrated bursts of energy"
+ icon_state = "pod_w_laser"
+ projectile_type = "/obj/item/projectile/beam"
+ shot_cost = 15
+ fire_sound = 'sound/weapons/Laser.ogg'
+ fire_delay = 25
\ No newline at end of file
diff --git a/code/WorkInProgress/pomf/spacepods/spacepods.dm b/code/WorkInProgress/pomf/spacepods/spacepods.dm
new file mode 100644
index 00000000000..aa84a437016
--- /dev/null
+++ b/code/WorkInProgress/pomf/spacepods/spacepods.dm
@@ -0,0 +1,435 @@
+// honk
+/obj/spacepod
+ name = "\improper space pod"
+ desc = "A space pod meant for space travel."
+ icon = 'icons/48x48/pods.dmi'
+ density = 1 //Dense. To raise the heat.
+ opacity = 0
+ anchored = 1
+ unacidable = 1
+ layer = 3.9
+ infra_luminosity = 15
+ var/mob/living/carbon/occupant
+ var/datum/spacepod/equipment/equipment_system
+ var/obj/item/weapon/cell/high/battery
+ var/datum/gas_mixture/cabin_air
+ var/obj/machinery/portable_atmospherics/canister/internal_tank
+ var/datum/effect/effect/system/ion_trail_follow/space_trail/ion_trail
+ var/use_internal_tank = 0
+ var/datum/global_iterator/pr_int_temp_processor //normalizes internal air mixture temperature
+ var/datum/global_iterator/pr_give_air //moves air from tank to cabin
+ var/inertia_dir = 0
+ var/hatch_open = 0
+ var/next_firetime = 0
+
+/obj/spacepod/New()
+ bound_width = 64
+ bound_height = 64
+ dir = EAST
+ battery = new()
+ add_cabin()
+ add_airtank()
+ src.ion_trail = new /datum/effect/effect/system/ion_trail_follow/space_trail()
+ src.ion_trail.set_up(src)
+ src.ion_trail.start()
+ src.use_internal_tank = 1
+ pr_int_temp_processor = new /datum/global_iterator/pod_preserve_temp(list(src))
+ pr_give_air = new /datum/global_iterator/pod_tank_give_air(list(src))
+ equipment_system = new(src)
+
+/obj/spacepod/attackby(obj/item/W as obj, mob/user as mob)
+ if(iscrowbar(W))
+ hatch_open = !hatch_open
+ user << "You [hatch_open ? "open" : "close"] the maintenance hatch."
+ if(istype(W, /obj/item/weapon/cell))
+ if(!hatch_open)
+ return ..()
+ if(battery)
+ user << "The pod already has a battery."
+ return
+ user.drop_item(W)
+ battery = W
+ W.loc = src
+ return
+ if(istype(W, /obj/item/device/spacepod_equipment))
+ if(!hatch_open)
+ return ..()
+ if(!equipment_system)
+ user << "The pod has no equipment datum, yell at pomf"
+ return
+ if(istype(W, /obj/item/device/spacepod_equipment/weaponry))
+ if(equipment_system.weapon_system)
+ user << "The pod already has a weapon system, remove it first."
+ return
+ else
+ user << "You insert \the [W] into the equipment system."
+ user.drop_item(W)
+ W.loc = equipment_system
+ equipment_system.weapon_system = W
+ verbs += /obj/spacepod/proc/fire_weapons
+ return
+
+/obj/spacepod/attack_hand(mob/user as mob)
+ if(!hatch_open)
+ return ..()
+ if(!equipment_system || !istype(equipment_system))
+ user << "The pod has no equpment datum, or is the wrong type, yell at pomf."
+ return
+ var/list/possible = list()
+ if(battery)
+ possible.Add("Energy Cell")
+ if(equipment_system.weapon_system)
+ possible.Add("Weapon System")
+ /* Not yet implemented
+ if(equipment_system.engine_system)
+ possible.Add("Engine System")
+ if(equipment_system.shield_system)
+ possible.Add("Shield System")
+ */
+ var/obj/item/device/spacepod_equipment/SPE
+ switch(input(user, "Remove which equipment?", null, null) as null|anything in possible)
+ if("Energy Cell")
+ if(user.put_in_any_hand_if_possible(battery))
+ user << "You remove \the [battery] from the space pod"
+ battery = null
+ if("Weapon System")
+ SPE = equipment_system.weapon_system
+ if(user.put_in_any_hand_if_possible(SPE))
+ user << "You remove \the [SPE] from the equipment system."
+ equipment_system.weapon_system = null
+ else
+ user << "You need an open hand to do that."
+ /*
+ if("engine system")
+ SPE = equipment_system.engine_system
+ if(user.put_in_any_hand_if_possible(SPE))
+ user << "You remove \the [SPE] from the equipment system."
+ equipment_system.engine_system = null
+ else
+ user << "You need an open hand to do that."
+ if("shield system")
+ SPE = equipment_system.shield_system
+ if(user.put_in_any_hand_if_possible(SPE))
+ user << "You remove \the [SPE] from the equipment system."
+ equipment_system.shield_system = null
+ else
+ user << "You need an open hand to do that."
+ */
+
+ return
+
+/obj/spacepod/civilian
+ icon_state = "pod_civ"
+ desc = "A sleek civilian space pod."
+/obj/spacepod/random
+ icon_state = "pod_civ"
+// placeholder
+/obj/spacepod/random/New()
+ ..()
+ icon_state = pick("pod_civ", "pod_black", "pod_mil", "pod_synd", "pod_gold", "pod_industrial")
+ switch(icon_state)
+ if("pod_civ")
+ desc = "A sleek civilian space pod."
+ if("pod_black")
+ desc = "An all black space pod with no insignias."
+ if("pod_mil")
+ desc = "A dark grey space pod brandishing the Nanotrasen Military insignia"
+ if("pod_synd")
+ desc = "A menacing military space pod with Fuck NT stenciled onto the side"
+ if("pod_gold")
+ desc = "A civilian space pod with a gold body, must have cost somebody a pretty penny"
+ if("pod_industrial")
+ desc = "A rough looking space pod meant for industrial work"
+
+/obj/spacepod/verb/toggle_internal_tank()
+ set name = "Toggle internal airtank usage"
+ set category = "Spacepod"
+ set src = usr.loc
+ set popup_menu = 0
+ if(usr!=src.occupant)
+ return
+ use_internal_tank = !use_internal_tank
+ src.occupant << "Now taking air from [use_internal_tank?"internal airtank":"environment"]."
+ return
+
+/obj/spacepod/proc/add_cabin()
+ cabin_air = new
+ cabin_air.temperature = T20C
+ cabin_air.volume = 200
+ cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
+ cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
+ return cabin_air
+
+/obj/spacepod/proc/add_airtank()
+ internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
+ return internal_tank
+
+/obj/spacepod/proc/get_turf_air()
+ var/turf/T = get_turf(src)
+ if(T)
+ . = T.return_air()
+ return
+
+/obj/spacepod/remove_air(amount)
+ if(use_internal_tank)
+ return cabin_air.remove(amount)
+ else
+ var/turf/T = get_turf(src)
+ if(T)
+ return T.remove_air(amount)
+ return
+
+/obj/spacepod/return_air()
+ if(use_internal_tank)
+ return cabin_air
+ return get_turf_air()
+
+/obj/spacepod/proc/return_pressure()
+ . = 0
+ if(use_internal_tank)
+ . = cabin_air.return_pressure()
+ else
+ var/datum/gas_mixture/t_air = get_turf_air()
+ if(t_air)
+ . = t_air.return_pressure()
+ return
+
+/obj/spacepod/proc/return_temperature()
+ . = 0
+ if(use_internal_tank)
+ . = cabin_air.return_temperature()
+ else
+ var/datum/gas_mixture/t_air = get_turf_air()
+ if(t_air)
+ . = t_air.return_temperature()
+ return
+
+/obj/spacepod/proc/moved_inside(var/mob/living/carbon/human/H as mob)
+ if(H && H.client && H in range(1))
+ H.reset_view(src)
+ /*
+ H.client.perspective = EYE_PERSPECTIVE
+ H.client.eye = src
+ */
+ H.stop_pulling()
+ H.forceMove(src)
+ src.occupant = H
+ src.add_fingerprint(H)
+ src.forceMove(src.loc)
+ //dir = dir_in
+ playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
+ return 1
+ else
+ return 0
+
+/obj/spacepod/MouseDrop_T(mob/M as mob, mob/user as mob)
+ if(M != user)
+ return
+ move_inside(M, user)
+
+/obj/spacepod/proc/fire_weapons()
+ set category = "Spacepod"
+ set name = "Fire Weapon System"
+ set desc = "Fire ze missiles(or lasers)"
+ set src = usr.loc
+
+ if(next_firetime > world.time)
+ usr << "Your weapons are recharging."
+ return
+ var/turf/firstloc
+ var/turf/secondloc
+ if(!equipment_system || !equipment_system.weapon_system)
+ usr << "Missing equipment or weapons."
+ src.verbs -= /obj/spacepod/proc/fire_weapons
+ return
+ battery.use(equipment_system.weapon_system.shot_cost)
+ var/olddir
+ for(var/i = 0; i < equipment_system.weapon_system.shots_per; i++)
+ if(olddir != dir)
+ switch(dir)
+ if(NORTH)
+ firstloc = get_step(src, NORTH)
+ secondloc = get_step(firstloc,EAST)
+ if(SOUTH)
+ firstloc = get_turf(src)
+ secondloc = get_step(firstloc,EAST)
+ if(EAST)
+ firstloc = get_turf(src)
+ firstloc = get_step(firstloc, EAST)
+ secondloc = get_step(firstloc,NORTH)
+ if(WEST)
+ firstloc = get_turf(src)
+ secondloc = get_step(firstloc,NORTH)
+ olddir = dir
+ var/obj/item/projectile/projone = new equipment_system.weapon_system.projectile_type(firstloc)
+ var/obj/item/projectile/projtwo = new equipment_system.weapon_system.projectile_type(secondloc)
+ projone.starting = get_turf(src)
+ projone.shot_from = src
+ projone.firer = usr
+ projone.def_zone = "chest"
+ projtwo.starting = get_turf(src)
+ projtwo.shot_from = src
+ projtwo.firer = usr
+ projtwo.def_zone = "chest"
+ spawn()
+ playsound(src, equipment_system.weapon_system.fire_sound, 50, 1)
+ projone.dumbfire(dir)
+ projtwo.dumbfire(dir)
+ sleep(1)
+ next_firetime = world.time + equipment_system.weapon_system.fire_delay
+/obj/spacepod/verb/move_inside()
+ set category = "Object"
+ set name = "Enter Pod"
+ set src in oview(1)
+
+ if(usr.restrained() || usr.stat || usr.weakened || usr.stunned || usr.paralysis || usr.resting) //are you cuffed, dying, lying, stunned or other
+ return
+ if (usr.stat || !ishuman(usr))
+ return
+ if (src.occupant)
+ usr << "\blue The [src.name] is already occupied!"
+ return
+/*
+ if (usr.abiotic())
+ usr << "\blue Subject cannot have abiotic items on."
+ return
+*/
+ for(var/mob/living/carbon/slime/M in range(1,usr))
+ if(M.Victim == usr)
+ usr << "You're too busy getting your life sucked out of you."
+ return
+// usr << "You start climbing into [src.name]"
+
+ visible_message("\blue [usr] starts to climb into [src.name]")
+
+ if(enter_after(40,usr))
+ if(!src.occupant)
+ moved_inside(usr)
+ else if(src.occupant!=usr)
+ usr << "[src.occupant] was faster. Try better next time, loser."
+ else
+ usr << "You stop entering the exosuit."
+ return
+
+/obj/spacepod/verb/exit_pod()
+ set name = "Exit pod"
+ set category = "Spacepod"
+ set src = usr.loc
+
+ if(usr != src.occupant)
+ return
+ inertia_dir = 0 // engage reverse thruster and power down pod
+ src.occupant.loc = src.loc
+ src.occupant = null
+ usr << "You climb out of the pod"
+ return
+
+/obj/spacepod/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5)
+ var/delayfraction = delay/numticks
+
+ var/turf/T = user.loc
+
+ for(var/i = 0, i 0)
+ var/delta = spacepod.cabin_air.temperature - T20C
+ spacepod.cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1)))
+ return
+
+/datum/global_iterator/pod_tank_give_air
+ delay = 15
+
+ process(var/obj/spacepod/spacepod)
+ if(spacepod.internal_tank)
+ var/datum/gas_mixture/tank_air = spacepod.internal_tank.return_air()
+ var/datum/gas_mixture/cabin_air = spacepod.cabin_air
+
+ var/release_pressure = ONE_ATMOSPHERE
+ var/cabin_pressure = cabin_air.return_pressure()
+ var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2)
+ var/transfer_moles = 0
+ if(pressure_delta > 0) //cabin pressure lower than release pressure
+ if(tank_air.return_temperature() > 0)
+ transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
+ var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
+ cabin_air.merge(removed)
+ else if(pressure_delta < 0) //cabin pressure higher than release pressure
+ var/datum/gas_mixture/t_air = spacepod.get_turf_air()
+ pressure_delta = cabin_pressure - release_pressure
+ if(t_air)
+ pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta)
+ if(pressure_delta > 0) //if location pressure is lower than cabin pressure
+ transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
+ var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles)
+ if(t_air)
+ t_air.merge(removed)
+ else //just delete the cabin gas, we're in space or some shit
+ del(removed)
+ else
+ return stop()
+ return
+
+/obj/spacepod/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0)
+ ..()
+ if(dir == 1 || dir == 4)
+ src.loc.Entered(src)
+/obj/spacepod/proc/Process_Spacemove(var/check_drift = 0, mob/user)
+ var/dense_object = 0
+ if(!user)
+ for(var/direction in list(NORTH, NORTHEAST, EAST))
+ var/turf/cardinal = get_step(src, direction)
+ if(istype(cardinal, /turf/space))
+ continue
+ dense_object++
+ break
+ if(!dense_object)
+ return 0
+ inertia_dir = 0
+ return 1
+
+/obj/spacepod/relaymove(mob/user, direction)
+ if(battery && battery.charge)
+ src.dir = direction
+ switch(direction)
+ if(1)
+ if(inertia_dir == 2)
+ inertia_dir = 0
+ return 0
+ if(2)
+ if(inertia_dir == 1)
+ inertia_dir = 0
+ return 0
+ if(4)
+ if(inertia_dir == 8)
+ inertia_dir = 0
+ return 0
+ if(8)
+ if(inertia_dir == 4)
+ inertia_dir = 0
+ return 0
+ step(src, direction)
+ if(istype(src.loc, /turf/space))
+ inertia_dir = direction
+ else
+ user << "She's dead, Jim"
+ return 0
+ battery.use(3)
+
+/obj/effect/landmark/spacepod/random
+ name = "spacepod spawner"
+ invisibility = 101
+ icon = 'icons/mob/screen1.dmi'
+ icon_state = "x"
+ anchored = 1
+
+/obj/effect/landmark/spacepod/random/New()
+ ..()
\ No newline at end of file
diff --git a/code/ZAS - oldbs12/Airflow.dm b/code/ZAS - oldbs12/Airflow.dm
new file mode 100644
index 00000000000..efa45700f4e
--- /dev/null
+++ b/code/ZAS - oldbs12/Airflow.dm
@@ -0,0 +1,420 @@
+/*
+
+CONTAINS:
+All AirflowX() procs, all Variable Setting Controls for airflow, save/load variable tweaks for airflow.
+
+VARIABLES:
+
+atom/movable/airflow_dest
+ The destination turf of a flying object.
+
+atom/movable/airflow_speed
+ The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time.
+
+
+OVERLOADABLE PROCS:
+
+mob/airflow_stun()
+ Contains checks for and results of being stunned by airflow.
+ Called when airflow quantities exceed airflow_medium_pressure.
+ RETURNS: Null
+
+atom/movable/check_airflow_movable(n)
+ Contains checks for moving any object due to airflow.
+ n is the pressure that is flowing.
+ RETURNS: 1 if the object moves under the air conditions, 0 if it stays put.
+
+atom/movable/airflow_hit(atom/A)
+ Contains results of hitting a solid object (A) due to airflow.
+ A is the dense object hit.
+ Use airflow_speed to determine how fast the projectile was going.
+
+
+AUTOMATIC PROCS:
+
+Airflow(zone/A, zone/B)
+ Causes objects to fly along a pressure gradient.
+ Called by zone updates. A and B are two connected zones.
+
+AirflowSpace(zone/A)
+ Causes objects to fly into space.
+ Called by zone updates. A is a zone connected to space.
+
+atom/movable/GotoAirflowDest(n)
+atom/movable/RepelAirflowDest(n)
+ Called by main airflow procs to cause the object to fly to or away from destination at speed n.
+ Probably shouldn't call this directly unless you know what you're
+ doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle.
+
+*/
+
+mob/var/tmp/last_airflow_stun = 0
+mob/proc/airflow_stun()
+ if(stat == 2)
+ return 0
+ if(last_airflow_stun > world.time - vsc.airflow_stun_cooldown) return 0
+ if(!(status_flags & CANSTUN) && !(status_flags & CANWEAKEN))
+ src << "\blue You stay upright as the air rushes past you."
+ return 0
+ if(weakened <= 0) src << "\red The sudden rush of air knocks you over!"
+ weakened = max(weakened,5)
+ last_airflow_stun = world.time
+
+mob/living/silicon/airflow_stun()
+ return
+
+mob/living/carbon/metroid/airflow_stun()
+ return
+
+mob/living/carbon/human/airflow_stun()
+ if(last_airflow_stun > world.time - vsc.airflow_stun_cooldown) return 0
+ if(buckled) return 0
+ if(shoes)
+ if((shoes.flags & NOSLIP) || (species.bodyflags & FEET_NOSLIP)) return 0
+ if(!(status_flags & CANSTUN) && !(status_flags & CANWEAKEN))
+ src << "\blue You stay upright as the air rushes past you."
+ return 0
+ if(weakened <= 0) src << "\red The sudden rush of air knocks you over!"
+ weakened = max(weakened,rand(1,5))
+ last_airflow_stun = world.time
+
+atom/movable/proc/check_airflow_movable(n)
+
+ if(anchored && !ismob(src)) return 0
+
+ if(!istype(src,/obj/item) && n < vsc.airflow_dense_pressure) return 0
+
+ return 1
+
+mob/check_airflow_movable(n)
+ if(n < vsc.airflow_heavy_pressure)
+ return 0
+ return 1
+
+mob/dead/observer/check_airflow_movable()
+ return 0
+
+mob/living/silicon/check_airflow_movable()
+ return 0
+
+
+obj/item/check_airflow_movable(n)
+ . = ..()
+ switch(w_class)
+ if(2)
+ if(n < vsc.airflow_lightest_pressure) return 0
+ if(3)
+ if(n < vsc.airflow_light_pressure) return 0
+ if(4,5)
+ if(n < vsc.airflow_medium_pressure) return 0
+
+//The main airflow code. Called by zone updates.
+//Zones A and B are air zones. n represents the amount of air moved.
+
+proc/Airflow(zone/A, zone/B)
+
+ var/n = B.air.return_pressure() - A.air.return_pressure()
+
+ //Don't go any further if n is lower than the lowest value needed for airflow.
+ if(abs(n) < vsc.airflow_lightest_pressure) return
+
+ //These turfs are the midway point between A and B, and will be the destination point for thrown objects.
+ var/list/connection/connections_A = A.connections
+ var/list/turf/connected_turfs = list()
+ for(var/connection/C in connections_A) //Grab the turf that is in the zone we are flowing to (determined by n)
+ if( ( A == C.A.zone || A == C.zone_A ) && ( B == C.B.zone || B == C.zone_B ) )
+ if(n < 0)
+ connected_turfs |= C.B
+ else
+ connected_turfs |= C.A
+ else if( ( A == C.B.zone || A == C.zone_B ) && ( B == C.A.zone || B == C.zone_A ) )
+ if(n < 0)
+ connected_turfs |= C.A
+ else
+ connected_turfs |= C.B
+
+ //Get lists of things that can be thrown across the room for each zone (assumes air is moving from zone B to zone A)
+ var/list/air_sucked = B.movables()
+ var/list/air_repelled = A.movables()
+ if(n < 0)
+ //air is moving from zone A to zone B
+ var/list/temporary_pplz = air_sucked
+ air_sucked = air_repelled
+ air_repelled = temporary_pplz
+
+ for(var/atom/movable/M in air_sucked)
+
+ if(M.last_airflow > world.time - vsc.airflow_delay) continue
+
+ //Check for knocking people over
+ if(ismob(M) && n > vsc.airflow_stun_pressure)
+ if(M:status_flags & GODMODE) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(n))
+
+ //Check for things that are in range of the midpoint turfs.
+ var/list/close_turfs = list()
+ for(var/turf/U in connected_turfs)
+ if(M in range(U)) close_turfs += U
+ if(!close_turfs.len) continue
+
+ //If they're already being tossed, don't do it again.
+ if(!M.airflow_speed)
+
+ M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+
+ spawn M.GotoAirflowDest(abs(n)/5)
+
+ //Do it again for the stuff in the other zone, making it fly away.
+ for(var/atom/movable/M in air_repelled)
+
+ if(M.last_airflow > world.time - vsc.airflow_delay) continue
+
+ if(ismob(M) && abs(n) > vsc.airflow_medium_pressure)
+ if(M:status_flags & GODMODE) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(abs(n)))
+
+ var/list/close_turfs = list()
+ for(var/turf/U in connected_turfs)
+ if(M in range(U)) close_turfs += U
+ if(!close_turfs.len) continue
+
+ //If they're already being tossed, don't do it again.
+ if(!M.airflow_speed)
+
+ M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+
+ spawn M.RepelAirflowDest(abs(n)/5)
+
+proc/AirflowSpace(zone/A)
+
+ //The space version of the Airflow(A,B,n) proc.
+
+ var/n = A.air.return_pressure()
+ //Here, n is determined by only the pressure in the room.
+
+ if(n < vsc.airflow_lightest_pressure) return
+
+ var/list/connected_turfs = A.unsimulated_tiles //The midpoints are now all the space connections.
+ var/list/pplz = A.movables() //We only need to worry about things in the zone, not things in space.
+
+ for(var/atom/movable/M in pplz)
+
+ if(M.last_airflow > world.time - vsc.airflow_delay) continue
+
+ if(ismob(M) && n > vsc.airflow_stun_pressure)
+ var/mob/O = M
+ if(O.status_flags & GODMODE) continue
+ O.airflow_stun()
+
+ if(M.check_airflow_movable(n))
+
+ var/list/close_turfs = list()
+ for(var/turf/U in connected_turfs)
+ if(M in range(U)) close_turfs += U
+ if(!close_turfs.len) continue
+
+ //If they're already being tossed, don't do it again.
+ if(!M.airflow_speed)
+
+ M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+ spawn
+ if(M) M.GotoAirflowDest(n/10)
+ //Sometimes shit breaks, and M isn't there after the spawn.
+
+
+/atom/movable/var/tmp/turf/airflow_dest
+/atom/movable/var/tmp/airflow_speed = 0
+/atom/movable/var/tmp/airflow_time = 0
+/atom/movable/var/tmp/last_airflow = 0
+
+/atom/movable/proc/GotoAirflowDest(n)
+ if(!airflow_dest) return
+ if(airflow_speed < 0) return
+ if(last_airflow > world.time - vsc.airflow_delay) return
+ if(airflow_speed)
+ airflow_speed = n/max(get_dist(src,airflow_dest),1)
+ return
+ last_airflow = world.time
+ if(airflow_dest == loc)
+ step_away(src,loc)
+ if(ismob(src))
+ if(src:status_flags & GODMODE)
+ return
+ if(istype(src, /mob/living/carbon/human))
+ if(src:buckled)
+ return
+ if(src:shoes)
+ if(istype(src:shoes, /obj/item/clothing/shoes/magboots))
+ if(src:shoes:magpulse)
+ return
+ src << "\red You are sucked away by airflow!"
+ var/airflow_falloff = 9 - ul_FalloffAmount(airflow_dest) //It's a fast falloff calc. Very useful.
+ if(airflow_falloff < 1)
+ airflow_dest = null
+ return
+ airflow_speed = min(max(n * (9/airflow_falloff),1),9)
+ var
+ xo = airflow_dest.x - src.x
+ yo = airflow_dest.y - src.y
+ od = 0
+ airflow_dest = null
+ if(!density)
+ density = 1
+ od = 1
+ while(airflow_speed > 0)
+ if(airflow_speed <= 0) return
+ airflow_speed = min(airflow_speed,15)
+ airflow_speed -= vsc.airflow_speed_decay
+ if(airflow_speed > 7)
+ if(airflow_time++ >= airflow_speed - 7)
+ if(od)
+ density = 0
+ sleep(1 * tick_multiplier)
+ else
+ if(od)
+ density = 0
+ sleep(max(1,10-(airflow_speed+3)) * tick_multiplier)
+ if(od)
+ density = 1
+ if ((!( src.airflow_dest ) || src.loc == src.airflow_dest))
+ src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z)
+ if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy))
+ return
+ if(!istype(loc, /turf))
+ return
+ step_towards(src, src.airflow_dest)
+ if(ismob(src) && src:client)
+ src:client:move_delay = world.time + vsc.airflow_mob_slowdown
+ airflow_dest = null
+ airflow_speed = 0
+ airflow_time = 0
+ if(od)
+ density = 0
+
+
+/atom/movable/proc/RepelAirflowDest(n)
+ if(!airflow_dest) return
+ if(airflow_speed < 0) return
+ if(last_airflow > world.time - vsc.airflow_delay) return
+ if(airflow_speed)
+ airflow_speed = n/max(get_dist(src,airflow_dest),1)
+ return
+ if(airflow_dest == loc)
+ step_away(src,loc)
+ if(ismob(src))
+ if(src:status_flags & GODMODE)
+ return
+ if(istype(src, /mob/living/carbon/human))
+ if(src:buckled)
+ return
+ if(src:shoes)
+ if(istype(src:shoes, /obj/item/clothing/shoes/magboots))
+ if(src:shoes.flags & NOSLIP)
+ return
+ src << "\red You are pushed away by airflow!"
+ last_airflow = world.time
+ var/airflow_falloff = 9 - ul_FalloffAmount(airflow_dest) //It's a fast falloff calc. Very useful.
+ if(airflow_falloff < 1)
+ airflow_dest = null
+ return
+ airflow_speed = min(max(n * (9/airflow_falloff),1),9)
+ var
+ xo = -(airflow_dest.x - src.x)
+ yo = -(airflow_dest.y - src.y)
+ od = 0
+ airflow_dest = null
+ if(!density)
+ density = 1
+ od = 1
+ while(airflow_speed > 0)
+ if(airflow_speed <= 0) return
+ airflow_speed = min(airflow_speed,15)
+ airflow_speed -= vsc.airflow_speed_decay
+ if(airflow_speed > 7)
+ if(airflow_time++ >= airflow_speed - 7)
+ sleep(1 * tick_multiplier)
+ else
+ sleep(max(1,10-(airflow_speed+3)) * tick_multiplier)
+ if ((!( src.airflow_dest ) || src.loc == src.airflow_dest))
+ src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z)
+ if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy))
+ return
+ if(!istype(loc, /turf))
+ return
+ step_towards(src, src.airflow_dest)
+ if(ismob(src) && src:client)
+ src:client:move_delay = world.time + vsc.airflow_mob_slowdown
+ airflow_dest = null
+ airflow_speed = 0
+ airflow_time = 0
+ if(od)
+ density = 0
+
+/atom/movable/Bump(atom/A)
+ if(airflow_speed > 0 && airflow_dest)
+ airflow_hit(A)
+ else
+ airflow_speed = 0
+ airflow_time = 0
+ . = ..()
+
+atom/movable/proc/airflow_hit(atom/A)
+ airflow_speed = 0
+ airflow_dest = null
+
+mob/airflow_hit(atom/A)
+ for(var/mob/M in hearers(src))
+ M.show_message("\red \The [src] slams into \a [A]!",1,"\red You hear a loud slam!",2)
+ playsound(src.loc, "smash.ogg", 25, 1, -1)
+ weakened = max(weakened, (istype(A,/obj/item) ? A:w_class : rand(1,5))) //Heheheh
+ . = ..()
+
+obj/airflow_hit(atom/A)
+ for(var/mob/M in hearers(src))
+ M.show_message("\red \The [src] slams into \a [A]!",1,"\red You hear a loud slam!",2)
+ playsound(src.loc, "smash.ogg", 25, 1, -1)
+ . = ..()
+
+obj/item/airflow_hit(atom/A)
+ airflow_speed = 0
+ airflow_dest = null
+
+mob/living/carbon/human/airflow_hit(atom/A)
+// for(var/mob/M in hearers(src))
+// M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2)
+ playsound(src.loc, "punch", 25, 1, -1)
+ loc:add_blood(src)
+ if (src.wear_suit)
+ src.wear_suit.add_blood(src)
+ if (src.w_uniform)
+ src.w_uniform.add_blood(src)
+ var/b_loss = airflow_speed * vsc.airflow_damage
+
+ var/blocked = run_armor_check("head","melee")
+ apply_damage(b_loss/3, BRUTE, "head", blocked, 0, "Airflow")
+
+ blocked = run_armor_check("chest","melee")
+ apply_damage(b_loss/3, BRUTE, "chest", blocked, 0, "Airflow")
+
+ blocked = run_armor_check("groin","melee")
+ apply_damage(b_loss/3, BRUTE, "groin", blocked, 0, "Airflow")
+
+ if(airflow_speed > 10)
+ paralysis += round(airflow_speed * vsc.airflow_stun)
+ stunned = max(stunned,paralysis + 3)
+ else
+ stunned += round(airflow_speed * vsc.airflow_stun/2)
+ . = ..()
+
+zone/proc/movables()
+ . = list()
+ for(var/turf/T in contents)
+ for(var/atom/A in T)
+// if(istype(A, /obj/effect) || istype(A, /mob/aiEye))
+ if(istype(A, /obj/effect) || istype(A, /mob/camera/aiEye))
+ continue
+ . += A
diff --git a/code/ZAS - oldbs12/Connection.dm b/code/ZAS - oldbs12/Connection.dm
new file mode 100644
index 00000000000..1bb8f9ca54d
--- /dev/null
+++ b/code/ZAS - oldbs12/Connection.dm
@@ -0,0 +1,448 @@
+/*
+This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked.
+Indirect connections will not merge the two zones after they reach equilibrium.
+*/
+#define CONNECTION_DIRECT 2
+#define CONNECTION_INDIRECT 1
+#define CONNECTION_CLOSED 0
+
+/connection
+ var/turf/simulated/A
+ var/turf/simulated/B
+
+ var/zone/zone_A
+ var/zone/zone_B
+
+ var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join.
+
+
+/connection/New(turf/T,turf/O)
+ . = ..()
+
+ A = T
+ B = O
+
+ if(A.zone && B.zone)
+ if(!A.zone.connections)
+ A.zone.connections = list()
+ A.zone.connections += src
+ zone_A = A.zone
+
+ if(!B.zone.connections)
+ B.zone.connections = list()
+ B.zone.connections += src
+ zone_B = B.zone
+
+ if(A in air_master.turfs_with_connections)
+ var/list/connections = air_master.turfs_with_connections[A]
+ connections.Add(src)
+ else
+ air_master.turfs_with_connections[A] = list(src)
+
+ if(B in air_master.turfs_with_connections)
+ var/list/connections = air_master.turfs_with_connections[B]
+ connections.Add(src)
+ else
+ air_master.turfs_with_connections[B] = list(src)
+
+ if(A.CanPass(null, B, 0, 0))
+
+ if(!A.CanPass(null, B, 1.5, 1))
+ indirect = CONNECTION_INDIRECT
+
+ ConnectZones(A.zone, B.zone, indirect)
+
+ else
+ ConnectZones(A.zone, B.zone)
+ indirect = CONNECTION_CLOSED
+
+ else
+ world.log << "Attempted to create connection object for non-zone tiles: [T] ([T.x],[T.y],[T.z]) -> [O] ([O.x],[O.y],[O.z])"
+ SoftDelete()
+
+
+/connection/Del()
+ //remove connections from master lists.
+ if(B in air_master.turfs_with_connections)
+ var/list/connections = air_master.turfs_with_connections[B]
+ connections.Remove(src)
+
+ if(A in air_master.turfs_with_connections)
+ var/list/connections = air_master.turfs_with_connections[A]
+ connections.Remove(src)
+
+ //Remove connection from zones.
+ if(A)
+ if(A.zone && A.zone.connections)
+ A.zone.connections.Remove(src)
+ if(!A.zone.connections.len)
+ A.zone.connections = null
+
+ if(istype(zone_A) && (!A || A.zone != zone_A))
+ if(zone_A.connections)
+ zone_A.connections.Remove(src)
+ if(!zone_A.connections.len)
+ zone_A.connections = null
+
+ if(B)
+ if(B.zone && B.zone.connections)
+ B.zone.connections.Remove(src)
+ if(!B.zone.connections.len)
+ B.zone.connections = null
+
+ if(istype(zone_B) && (!B || B.zone != zone_B))
+ if(zone_B.connections)
+ zone_B.connections.Remove(src)
+ if(!zone_B.connections.len)
+ zone_B.connections = null
+
+ //Disconnect zones while handling unusual conditions.
+ // e.g. loss of a zone on a turf
+ DisconnectZones(zone_A, zone_B)
+
+ //Finally, preform actual deletion.
+ . = ..()
+
+
+/connection/proc/SoftDelete()
+ //remove connections from master lists.
+ if(B in air_master.turfs_with_connections)
+ var/list/connections = air_master.turfs_with_connections[B]
+ connections.Remove(src)
+
+ if(A in air_master.turfs_with_connections)
+ var/list/connections = air_master.turfs_with_connections[A]
+ connections.Remove(src)
+
+ //Remove connection from zones.
+ if(A)
+ if(A.zone && A.zone.connections)
+ A.zone.connections.Remove(src)
+ if(!A.zone.connections.len)
+ A.zone.connections = null
+
+ if(istype(zone_A) && (!A || A.zone != zone_A))
+ if(zone_A.connections)
+ zone_A.connections.Remove(src)
+ if(!zone_A.connections.len)
+ zone_A.connections = null
+
+ if(B)
+ if(B.zone && B.zone.connections)
+ B.zone.connections.Remove(src)
+ if(!B.zone.connections.len)
+ B.zone.connections = null
+
+ if(istype(zone_B) && (!B || B.zone != zone_B))
+ if(zone_B.connections)
+ zone_B.connections.Remove(src)
+ if(!zone_B.connections.len)
+ zone_B.connections = null
+
+ //Disconnect zones while handling unusual conditions.
+ // e.g. loss of a zone on a turf
+ DisconnectZones(zone_A, zone_B)
+
+
+/connection/proc/ConnectZones(var/zone/zone_1, var/zone/zone_2, open = 0)
+
+ //Sanity checking
+ if(!istype(zone_1) || !istype(zone_2))
+ return
+
+ //Handle zones connecting indirectly/directly.
+ if(open)
+
+ //Create the lists if necessary.
+ if(!zone_1.connected_zones)
+ zone_1.connected_zones = list()
+
+ if(!zone_2.connected_zones)
+ zone_2.connected_zones = list()
+
+ //Increase the number of connections between zones.
+ if(zone_2 in zone_1.connected_zones)
+ zone_1.connected_zones[zone_2]++
+ else
+ zone_1.connected_zones += zone_2
+ zone_1.connected_zones[zone_2] = 1
+
+ if(zone_1 in zone_2.connected_zones)
+ zone_2.connected_zones[zone_1]++
+ else
+ zone_2.connected_zones += zone_1
+ zone_2.connected_zones[zone_1] = 1
+
+ if(open == CONNECTION_DIRECT)
+ if(!zone_1.direct_connections)
+ zone_1.direct_connections = list(src)
+ else
+ zone_1.direct_connections += src
+
+ if(!zone_2.direct_connections)
+ zone_2.direct_connections = list(src)
+ else
+ zone_2.direct_connections += src
+
+
+ //Handle closed connections.
+ else
+
+ //Create the lists
+ if(!zone_1.closed_connection_zones)
+ zone_1.closed_connection_zones = list()
+
+ if(!zone_2.closed_connection_zones)
+ zone_2.closed_connection_zones = list()
+
+ //Increment the connections.
+ if(zone_2 in zone_1.closed_connection_zones)
+ zone_1.closed_connection_zones[zone_2]++
+ else
+ zone_1.closed_connection_zones += zone_2
+ zone_1.closed_connection_zones[zone_2] = 1
+
+ if(zone_1 in zone_2.closed_connection_zones)
+ zone_2.closed_connection_zones[zone_1]++
+ else
+ zone_2.closed_connection_zones += zone_1
+ zone_2.closed_connection_zones[zone_1] = 1
+
+ if(zone_1.status == ZONE_SLEEPING)
+ zone_1.SetStatus(ZONE_ACTIVE)
+
+ if(zone_2.status == ZONE_SLEEPING)
+ zone_2.SetStatus(ZONE_ACTIVE)
+
+/connection/proc/DisconnectZones(var/zone/zone_1, var/zone/zone_2)
+ //Sanity checking
+ if(!istype(zone_1) || !istype(zone_2))
+ return
+
+ if(indirect != CONNECTION_CLOSED)
+ //Handle disconnection of indirectly or directly connected zones.
+ if( (zone_1 in zone_2.connected_zones) || (zone_2 in zone_1.connected_zones) )
+
+ //If there are more than one connection, decrement the number of connections
+ //Otherwise, remove all connections between the zones.
+ if(zone_2 in zone_1.connected_zones)
+ if(zone_1.connected_zones[zone_2] > 1)
+ zone_1.connected_zones[zone_2]--
+ else
+ zone_1.connected_zones -= zone_2
+ //remove the list if it is empty
+ if(!zone_1.connected_zones.len)
+ zone_1.connected_zones = null
+
+ //Then do the same for the other zone.
+ if(zone_1 in zone_2.connected_zones)
+ if(zone_2.connected_zones[zone_1] > 1)
+ zone_2.connected_zones[zone_1]--
+ else
+ zone_2.connected_zones -= zone_1
+ if(!zone_2.connected_zones.len)
+ zone_2.connected_zones = null
+
+ if(indirect == CONNECTION_DIRECT)
+ zone_1.direct_connections -= src
+ if(!zone_1.direct_connections.len)
+ zone_1.direct_connections = null
+
+ zone_2.direct_connections -= src
+ if(!zone_2.direct_connections.len)
+ zone_2.direct_connections = null
+
+ else
+ //Handle disconnection of closed zones.
+ if( (zone_1 in zone_2.closed_connection_zones) || (zone_2 in zone_1.closed_connection_zones) )
+
+ //If there are more than one connection, decrement the number of connections
+ //Otherwise, remove all connections between the zones.
+ if(zone_2 in zone_1.closed_connection_zones)
+ if(zone_1.closed_connection_zones[zone_2] > 1)
+ zone_1.closed_connection_zones[zone_2]--
+ else
+ zone_1.closed_connection_zones -= zone_2
+ //remove the list if it is empty
+ if(!zone_1.closed_connection_zones.len)
+ zone_1.closed_connection_zones = null
+
+ //Then do the same for the other zone.
+ if(zone_1 in zone_2.closed_connection_zones)
+ if(zone_2.closed_connection_zones[zone_1] > 1)
+ zone_2.closed_connection_zones[zone_1]--
+ else
+ zone_2.closed_connection_zones -= zone_1
+ if(!zone_2.closed_connection_zones.len)
+ zone_2.closed_connection_zones = null
+
+
+/connection/proc/Cleanup()
+
+ //Check sanity: existance of turfs
+ if(!A || !B)
+ SoftDelete()
+ return
+
+ //Check sanity: loss of zone
+ if(!A.zone || !B.zone)
+ SoftDelete()
+ return
+
+ //Check sanity: zones are different
+ if(A.zone == B.zone)
+ SoftDelete()
+ return
+
+ //Handle zones changing on a turf.
+ if((A.zone && A.zone != zone_A) || (B.zone && B.zone != zone_B))
+ Sanitize()
+
+ if(A.zone && B.zone)
+
+ //If no walls are blocking us...
+ if(A.ZAirPass(B))
+ //...we check to see if there is a door in the way...
+ var/door_pass = A.CanPass(null,B,1.5,1)
+ //...and if it is opened.
+ if(door_pass || A.CanPass(null,B,0,0))
+
+ //Make and remove connections to let air pass.
+ if(indirect == CONNECTION_CLOSED)
+ DisconnectZones(A.zone, B.zone)
+ ConnectZones(A.zone, B.zone, door_pass + 1)
+
+ if(door_pass)
+ indirect = CONNECTION_DIRECT
+ else if(!door_pass)
+ indirect = CONNECTION_INDIRECT
+
+ //The door is instead closed.
+ else if(indirect > CONNECTION_CLOSED)
+ DisconnectZones(A.zone, B.zone)
+ indirect = CONNECTION_CLOSED
+ ConnectZones(A.zone, B.zone)
+
+ //If I can no longer pass air, better delete
+ else
+ SoftDelete()
+ return
+
+/connection/proc/Sanitize()
+ //If the zones change on connected turfs, update it.
+
+ //Both zones changed (wat)
+ if(A.zone && A.zone != zone_A && B.zone && B.zone != zone_B)
+
+ //If the zones have gotten swapped
+ // (do not ask me how, I am just being anal retentive about sanity)
+ if(A.zone == zone_B && B.zone == zone_A)
+ var/turf/temp = B
+ B = A
+ A = temp
+ zone_B = B.zone
+ zone_A = A.zone
+ return
+
+ //Handle removal of connections from archived zones.
+ if(zone_A && zone_A.connections)
+ zone_A.connections.Remove(src)
+ if(!zone_A.connections.len)
+ zone_A.connections = null
+
+ if(zone_B && zone_B.connections)
+ zone_B.connections.Remove(src)
+ if(!zone_B.connections.len)
+ zone_B.connections = null
+
+ if(A.zone)
+ if(!A.zone.connections)
+ A.zone.connections = list()
+ A.zone.connections |= src
+
+ if(B.zone)
+ if(!B.zone.connections)
+ B.zone.connections = list()
+ B.zone.connections |= src
+
+ //If either zone is null, we disconnect the archived ones after cleaning up the connections.
+ if(!A.zone || !B.zone)
+ if(zone_A && zone_B)
+ DisconnectZones(zone_B, zone_A)
+
+ if(!A.zone)
+ zone_A = A.zone
+
+ if(!B.zone)
+ zone_B = B.zone
+ return
+
+ //Handle diconnection and reconnection of zones.
+ if(zone_A && zone_B)
+ DisconnectZones(zone_A, zone_B)
+ ConnectZones(A.zone, B.zone, indirect)
+
+ //resetting values of archived values.
+ zone_B = B.zone
+ zone_A = A.zone
+
+ //The "A" zone changed.
+ else if(A.zone && A.zone != zone_A)
+
+ //Handle connection cleanup
+ if(zone_A)
+ if(zone_A.connections)
+ zone_A.connections.Remove(src)
+ if(!zone_A.connections.len)
+ zone_A.connections = null
+
+ if(A.zone)
+ if(!A.zone.connections)
+ A.zone.connections = list()
+ A.zone.connections |= src
+
+ //If the "A" zone is null, we disconnect the archived ones after cleaning up the connections.
+ if(!A.zone)
+ if(zone_A && zone_B)
+ DisconnectZones(zone_A, zone_B)
+ zone_A = A.zone
+ return
+
+ //Handle diconnection and reconnection of zones.
+ if(zone_A && zone_B)
+ DisconnectZones(zone_A, zone_B)
+ ConnectZones(A.zone, B.zone, indirect)
+ zone_A = A.zone
+
+ //The "B" zone changed.
+ else if(B.zone && B.zone != zone_B)
+
+ //Handle connection cleanup
+ if(zone_B)
+ if(zone_B.connections)
+ zone_B.connections.Remove(src)
+ if(!zone_B.connections.len)
+ zone_B.connections = null
+
+ if(B.zone)
+ if(!B.zone.connections)
+ B.zone.connections = list()
+ B.zone.connections |= src
+
+ //If the "B" zone is null, we disconnect the archived ones after cleaning up the connections.
+ if(!B.zone)
+ if(zone_A && zone_B)
+ DisconnectZones(zone_A, zone_B)
+ zone_B = B.zone
+ return
+
+ //Handle diconnection and reconnection of zones.
+ if(zone_A && zone_B)
+ DisconnectZones(zone_A, zone_B)
+ ConnectZones(A.zone, B.zone, indirect)
+ zone_B = B.zone
+
+
+#undef CONNECTION_DIRECT
+#undef CONNECTION_INDIRECT
+#undef CONNECTION_CLOSED
diff --git a/code/ZAS - oldbs12/Debug.dm b/code/ZAS - oldbs12/Debug.dm
new file mode 100644
index 00000000000..39f5a7caabd
--- /dev/null
+++ b/code/ZAS - oldbs12/Debug.dm
@@ -0,0 +1,219 @@
+client/proc/ZoneTick()
+ set category = "Debug"
+ set name = "Process Atmos"
+
+ var/result = air_master.Tick()
+ if(result)
+ src << "Sucessfully Processed."
+
+ else
+ src << "Failed to process! ([air_master.tick_progress])"
+
+
+client/proc/Zone_Info(turf/T as null|turf)
+ set category = "Debug"
+ if(T)
+ if(T.zone)
+ T.zone.DebugDisplay(src)
+ else
+ mob << "No zone here."
+ else
+ if(zone_debug_images)
+ for(var/zone in zone_debug_images)
+ images -= zone_debug_images[zone]
+ zone_debug_images = null
+
+client/var/list/zone_debug_images
+
+client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
+ set category = "Debug"
+ if(!istype(T))
+ return
+
+ var/direction_list = list(\
+ "North" = NORTH,\
+ "South" = SOUTH,\
+ "East" = EAST,\
+ "West" = WEST,\
+ "N/A" = null)
+ var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
+ if(!direction)
+ return
+
+ if(direction == "N/A")
+ if(T.CanPass(null, T, 0,0))
+ mob << "The turf can pass air! :D"
+ else
+ mob << "No air passage :x"
+ return
+
+ var/turf/simulated/other_turf = get_step(T, direction_list[direction])
+ if(!istype(other_turf))
+ return
+
+ var/pass_directions = T.CanPass(null, other_turf, 0, 0) + 2 * other_turf.CanPass(null, T, 0, 0)
+
+ switch(pass_directions)
+ if(0)
+ mob << "Neither turf can connect. :("
+
+ if(1)
+ mob << "The initial turf only can connect. :\\"
+
+ if(2)
+ mob << "The other turf can connect, but not the initial turf. :/"
+
+ if(3)
+ mob << "Both turfs can connect! :)"
+
+
+zone/proc/DebugDisplay(client/client)
+ if(!istype(client))
+ return
+
+ if(!dbg_output)
+ dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
+
+ if(!client.zone_debug_images)
+ client.zone_debug_images = list()
+
+ var/list/current_zone_images = list()
+
+ for(var/turf/T in contents)
+ current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
+
+ for(var/turf/space/S in unsimulated_tiles)
+ current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
+
+ client << "Zone Air Contents"
+ client << "Oxygen: [air.oxygen]"
+ client << "Nitrogen: [air.nitrogen]"
+ client << "Plasma: [air.toxins]"
+ client << "Carbon Dioxide: [air.carbon_dioxide]"
+ client << "Temperature: [air.temperature] K"
+ client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
+ client << "Pressure: [air.return_pressure()] KPa"
+ client << ""
+ client << "Space Tiles: [length(unsimulated_tiles)]"
+ client << "Movable Objects: [length(movables())]"
+ client << "Connections: [length(connections)]"
+
+ for(var/connection/C in connections)
+ client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
+ current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
+ current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
+
+ client << "Connected Zones:"
+ for(var/zone/zone in connected_zones)
+ client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
+
+ for(var/zone/zone in closed_connection_zones)
+ client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
+
+ for(var/C in connections)
+ if(!istype(C,/connection))
+ client << "[C] (Not Connection!)"
+
+ if(!client.zone_debug_images)
+ client.zone_debug_images = list()
+ client.zone_debug_images[src] = current_zone_images
+
+ client.images += client.zone_debug_images[src]
+
+ else
+ dbg_output = 0
+
+ client.images -= client.zone_debug_images[src]
+ client.zone_debug_images.Remove(src)
+
+ if(air_master)
+ for(var/zone/Z in air_master.zones)
+ if(Z.air == air && Z != src)
+ var/turf/zloc = pick(Z.contents)
+ client << "\red Illegal air datum shared by: [zloc.loc.name]"
+
+
+client/proc/TestZASRebuild()
+ set category = "Debug"
+// var/turf/turf = get_turf(mob)
+ var/zone/current_zone = mob.loc:zone
+ if(!current_zone)
+ src << "There is no zone there!"
+ return
+
+ var/list/current_adjacents = list()
+ var/list/overlays = list()
+ var/adjacent_id
+ var/lowest_id
+
+ var/list/identical_ids = list()
+ var/list/turfs = current_zone.contents.Copy()
+ var/current_identifier = 1
+
+ for(var/turf/simulated/current in turfs)
+ lowest_id = null
+ current_adjacents = list()
+
+ for(var/direction in cardinal)
+ var/turf/simulated/adjacent = get_step(current, direction)
+ if(!current.ZCanPass(adjacent))
+ continue
+ if(turfs.Find(adjacent))
+ current_adjacents += adjacent
+ adjacent_id = turfs[adjacent]
+
+ if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
+ lowest_id = adjacent_id
+
+ if(!lowest_id)
+ lowest_id = current_identifier++
+ identical_ids += lowest_id
+ overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
+
+ for(var/turf/simulated/adjacent in current_adjacents)
+ adjacent_id = turfs[adjacent]
+ if(adjacent_id != lowest_id)
+ if(adjacent_id)
+ adjacent.overlays -= overlays[adjacent_id]
+ identical_ids[adjacent_id] = lowest_id
+
+ turfs[adjacent] = lowest_id
+ adjacent.overlays += overlays[lowest_id]
+
+ sleep(5)
+
+ if(turfs[current])
+ current.overlays -= overlays[turfs[current]]
+ turfs[current] = lowest_id
+ current.overlays += overlays[lowest_id]
+ sleep(5)
+
+ var/list/final_arrangement = list()
+
+ for(var/turf/simulated/current in turfs)
+ current_identifier = identical_ids[turfs[current]]
+ current.overlays -= overlays[turfs[current]]
+ current.overlays += overlays[current_identifier]
+ sleep(5)
+
+ if( current_identifier > final_arrangement.len )
+ final_arrangement.len = current_identifier
+ final_arrangement[current_identifier] = list(current)
+
+ else
+ final_arrangement[current_identifier] += current
+
+ //lazy but fast
+ final_arrangement.Remove(null)
+
+ src << "There are [final_arrangement.len] unique segments."
+
+ for(var/turf/current in turfs)
+ current.overlays -= overlays
+
+ return final_arrangement
+
+/client/proc/ZASSettings()
+ set category = "Debug"
+
+ vsc.SetDefault(mob)
\ No newline at end of file
diff --git a/code/ZAS/FEA_gas_mixture.dm b/code/ZAS - oldbs12/FEA_gas_mixture.dm
similarity index 100%
rename from code/ZAS/FEA_gas_mixture.dm
rename to code/ZAS - oldbs12/FEA_gas_mixture.dm
diff --git a/code/ZAS/FEA_system.dm b/code/ZAS - oldbs12/FEA_system.dm
similarity index 100%
rename from code/ZAS/FEA_system.dm
rename to code/ZAS - oldbs12/FEA_system.dm
diff --git a/code/ZAS - oldbs12/Fire.dm b/code/ZAS - oldbs12/Fire.dm
new file mode 100644
index 00000000000..b79d7f8b553
--- /dev/null
+++ b/code/ZAS - oldbs12/Fire.dm
@@ -0,0 +1,362 @@
+/*
+
+Making Bombs with ZAS:
+Make burny fire with lots of burning
+Draw off 5000K gas from burny fire
+Separate gas into oxygen and plasma components
+Obtain plasma and oxygen tanks filled up about 50-75% with normal-temp gas
+Fill rest with super hot gas from separated canisters, they should be about 125C now.
+Attach to transfer valve and open. BOOM.
+
+*/
+
+
+//Some legacy definitions so fires can be started.
+atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ return null
+
+
+turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
+
+
+turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
+ if(fire_protection > world.time-300)
+ return 0
+ if(locate(/obj/fire) in src)
+ return 1
+ var/datum/gas_mixture/air_contents = return_air()
+ if(!air_contents || exposed_temperature < PLASMA_MINIMUM_BURN_TEMPERATURE)
+ return 0
+
+ var/obj/structure/reagent_dispensers/fueltank/FT = locate() in src
+ if(exposed_temperature >= AUTOIGNITION_WELDERFUEL)
+ if (FT)
+ FT.explode()
+ return 1
+
+ var/igniting = 0
+ var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in src
+
+ if(air_contents.check_combustability(liquid))
+ igniting = 1
+
+ if(! (locate(/obj/fire) in src))
+
+ new /obj/fire(src,1000)
+
+ return igniting
+
+/obj/fire
+ //Icon for fire on turfs.
+
+ anchored = 1
+ mouse_opacity = 0
+
+ //luminosity = 3
+
+ icon = 'icons/effects/fire.dmi'
+ icon_state = "1"
+
+ layer = TURF_LAYER
+
+ var/firelevel = 10000 //Calculated by gas_mixture.calculate_firelevel()
+
+/obj/fire/process()
+ . = 1
+
+ //get location and check if it is in a proper ZAS zone
+ var/turf/simulated/S = loc
+
+ if(!istype(S))
+ del src
+
+ if(!S.zone)
+ del src
+
+ var/datum/gas_mixture/air_contents = S.return_air()
+ //get liquid fuels on the ground.
+ var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in S
+ //and the volatile stuff from the air
+ var/datum/gas/volatile_fuel/fuel = locate() in air_contents.trace_gases
+
+ //since the air is processed in fractions, we need to make sure not to have any minuscle residue or
+ //the amount of moles might get to low for some functions to catch them and thus result in wonky behaviour
+ if(air_contents.oxygen < 0.001)
+ air_contents.oxygen = 0
+ if(air_contents.toxins < 0.001)
+ air_contents.toxins = 0
+ if(fuel)
+ if(fuel.moles < 0.001)
+ air_contents.trace_gases.Remove(fuel)
+
+ //check if there is something to combust
+ if(!air_contents.check_recombustability(liquid))
+ //del src
+ RemoveFire()
+
+ //get a firelevel and set the icon
+ firelevel = air_contents.calculate_firelevel(liquid)
+
+ if(firelevel > 6)
+ icon_state = "3"
+ SetLuminosity(7)
+ else if(firelevel > 2.5)
+ icon_state = "2"
+ SetLuminosity(5)
+ else
+ icon_state = "1"
+ SetLuminosity(3)
+
+ //im not sure how to implement a version that works for every creature so for now monkeys are firesafe
+ for(var/mob/living/carbon/human/M in loc)
+ M.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure() ) //Burn the humans!
+ for(var/atom/A in loc)
+ A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
+ //spread
+ for(var/direction in cardinal)
+ if(S.air_check_directions&direction) //Grab all valid bordering tiles
+
+ var/turf/simulated/enemy_tile = get_step(S, direction)
+
+ if(istype(enemy_tile))
+ var/datum/gas_mixture/acs = enemy_tile.return_air()
+ var/obj/effect/decal/cleanable/liquid_fuel/liq = locate() in enemy_tile
+ if(!acs) continue
+ if(!acs.check_recombustability(liq)) continue
+ //If extinguisher mist passed over the turf it's trying to spread to, don't spread and
+ //reduce firelevel.
+ if(enemy_tile.fire_protection > world.time-30)
+ firelevel -= 1.5
+ continue
+
+ //Spread the fire.
+ if(!(locate(/obj/fire) in enemy_tile))
+ if( prob( 50 + 50 * (firelevel/vsc.fire_firelevel_multiplier) ) && S.CanPass(null, enemy_tile, 0,0) && enemy_tile.CanPass(null, S, 0,0))
+ new/obj/fire(enemy_tile,firelevel)
+
+ //seperate part of the present gas
+ //this is done to prevent the fire burning all gases in a single pass
+ var/datum/gas_mixture/flow = air_contents.remove_ratio(vsc.fire_consuption_rate)
+///////////////////////////////// FLOW HAS BEEN CREATED /// DONT DELETE THE FIRE UNTIL IT IS MERGED BACK OR YOU WILL DELETE AIR ///////////////////////////////////////////////
+
+ if(flow)
+
+ if(flow.check_recombustability(liquid))
+ //Ensure flow temperature is higher than minimum fire temperatures.
+ //this creates some energy ex nihilo but is necessary to get a fire started
+ //lets just pretend this energy comes from the ignition source and dont mention this again
+ //flow.temperature = max(PLASMA_MINIMUM_BURN_TEMPERATURE+0.1,flow.temperature)
+
+ //burn baby burn!
+
+ flow.zburn(liquid,1)
+ //merge the air back
+ S.assume_air(flow)
+
+///////////////////////////////// FLOW HAS BEEN REMERGED /// feel free to delete the fire again from here on //////////////////////////////////////////////////////////////////
+
+
+/obj/fire/New(newLoc,fl)
+ ..()
+
+ if(!istype(loc, /turf))
+ del src
+
+ dir = pick(cardinal)
+ SetLuminosity(3)
+ firelevel = fl
+ air_master.active_hotspots.Add(src)
+
+
+/obj/fire/Destroy()
+ if (istype(loc, /turf/simulated))
+ SetLuminosity(0)
+
+ loc = null
+ air_master.active_hotspots.Remove(src)
+
+ ..()
+
+/obj/fire/proc/RemoveFire()
+ if (istype(loc, /turf/simulated))
+ SetLuminosity(0)
+ loc = null
+ air_master.active_hotspots.Remove(src)
+
+
+
+turf/simulated/var/fire_protection = 0 //Protects newly extinguished tiles from being overrun again.
+turf/proc/apply_fire_protection()
+turf/simulated/apply_fire_protection()
+ fire_protection = world.time
+
+
+datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, force_burn)
+ var/value = 0
+
+ if((temperature > PLASMA_MINIMUM_BURN_TEMPERATURE || force_burn) && check_recombustability(liquid))
+ var/total_fuel = 0
+ var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+
+ total_fuel += toxins
+
+ if(fuel)
+ //Volatile Fuel
+ total_fuel += fuel.moles
+
+ if(liquid)
+ //Liquid Fuel
+ if(liquid.amount <= 0)
+ del liquid
+ else
+ total_fuel += liquid.amount
+
+ //Calculate the firelevel.
+ var/firelevel = calculate_firelevel(liquid)
+
+ //get the current inner energy of the gas mix
+ //this must be taken here to prevent the addition or deletion of energy by a changing heat capacity
+ var/starting_energy = temperature * heat_capacity()
+
+ //determine the amount of oxygen used
+ var/total_oxygen = min(oxygen, 2 * total_fuel)
+
+ //determine the amount of fuel actually used
+ var/used_fuel_ratio = min(oxygen / 2 , total_fuel) / total_fuel
+ total_fuel = total_fuel * used_fuel_ratio
+
+ var/total_reactants = total_fuel + total_oxygen
+
+ //determine the amount of reactants actually reacting
+ var/used_reactants_ratio = min( max(total_reactants * firelevel / vsc.fire_firelevel_multiplier, 0.2), total_reactants) / total_reactants
+
+ //remove and add gasses as calculated
+ oxygen -= min(oxygen, total_oxygen * used_reactants_ratio )
+
+ toxins -= min(toxins, (toxins * used_fuel_ratio * used_reactants_ratio ) * 3)
+ if(toxins < 0)
+ toxins = 0
+
+ carbon_dioxide += max(2 * total_fuel, 0)
+
+ if(fuel)
+ fuel.moles -= (fuel.moles * used_fuel_ratio * used_reactants_ratio) * 5 //Fuel burns 5 times as quick
+ if(fuel.moles <= 0) del fuel
+
+ if(liquid)
+ liquid.amount -= (liquid.amount * used_fuel_ratio * used_reactants_ratio) * 5 // liquid fuel burns 5 times as quick
+
+ if(liquid.amount <= 0) del liquid
+
+ //calculate the energy produced by the reaction and then set the new temperature of the mix
+ temperature = (starting_energy + vsc.fire_fuel_energy_release * total_fuel) / heat_capacity()
+
+ update_values()
+ value = total_reactants * used_reactants_ratio
+ return value
+
+datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_fuel/liquid)
+ //this is a copy proc to continue a fire after its been started.
+
+ var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+
+ if(oxygen && (toxins || fuel || liquid))
+ if(liquid)
+ return 1
+ if (toxins)
+ return 1
+ if(fuel)
+ return 1
+
+ return 0
+
+datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid)
+ //this check comes up very often and is thus centralized here to ease adding stuff
+
+ var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+
+ if(oxygen && (toxins || fuel || liquid))
+ if(liquid)
+ return 1
+ if (toxins >= 0.7)
+ return 1
+ if(fuel && fuel.moles >= 1.4)
+ return 1
+
+ return 0
+
+datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid)
+ //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas.
+
+ var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+ var/total_fuel = 0
+ var/firelevel = 0
+
+ if(check_recombustability(liquid))
+
+ total_fuel += toxins
+
+ if(liquid)
+ total_fuel += liquid.amount
+
+ if(fuel)
+ total_fuel += fuel.moles
+
+ var/total_combustables = (total_fuel + oxygen)
+
+ if(total_fuel > 0 && oxygen > 0)
+
+ //slows down the burning when the concentration of the reactants is low
+ var/dampening_multiplier = total_combustables / (total_combustables + nitrogen + carbon_dioxide)
+ //calculates how close the mixture of the reactants is to the optimum
+ var/mix_multiplier = 1 / (1 + (5 * ((oxygen / total_combustables) ** 2)))
+ //toss everything together
+ firelevel = vsc.fire_firelevel_multiplier * mix_multiplier * dampening_multiplier
+
+ return max( 0, firelevel)
+
+
+/mob/living/proc/FireBurn(var/firelevel, var/last_temperature, var/pressure)
+ var/mx = 5 * firelevel/vsc.fire_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1)
+ apply_damage(2.5*mx, BURN)
+
+
+/mob/living/carbon/human/FireBurn(var/firelevel, var/last_temperature, var/pressure)
+ //Burns mobs due to fire. Respects heat transfer coefficients on various body parts.
+ //Due to TG reworking how fireprotection works, this is kinda less meaningful.
+
+ var/head_exposure = 1
+ var/chest_exposure = 1
+ var/groin_exposure = 1
+ var/legs_exposure = 1
+ var/arms_exposure = 1
+
+ //Get heat transfer coefficients for clothing.
+
+ for(var/obj/item/clothing/C in src)
+ if(l_hand == C || r_hand == C)
+ continue
+
+ if( C.max_heat_protection_temperature >= last_temperature )
+ if(C.body_parts_covered & HEAD)
+ head_exposure = 0
+ if(C.body_parts_covered & UPPER_TORSO)
+ chest_exposure = 0
+ if(C.body_parts_covered & LOWER_TORSO)
+ groin_exposure = 0
+ if(C.body_parts_covered & LEGS)
+ legs_exposure = 0
+ if(C.body_parts_covered & ARMS)
+ arms_exposure = 0
+ //minimize this for low-pressure enviroments
+ var/mx = 5 * firelevel/vsc.fire_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1)
+
+ //Always check these damage procs first if fire damage isn't working. They're probably what's wrong.
+
+ apply_damage(2.5*mx*head_exposure, BURN, "head", 0, 0, "Fire")
+ apply_damage(2.5*mx*chest_exposure, BURN, "chest", 0, 0, "Fire")
+ apply_damage(2.0*mx*groin_exposure, BURN, "groin", 0, 0, "Fire")
+ apply_damage(0.6*mx*legs_exposure, BURN, "l_leg", 0, 0, "Fire")
+ apply_damage(0.6*mx*legs_exposure, BURN, "r_leg", 0, 0, "Fire")
+ apply_damage(0.4*mx*arms_exposure, BURN, "l_arm", 0, 0, "Fire")
+ apply_damage(0.4*mx*arms_exposure, BURN, "r_arm", 0, 0, "Fire")
diff --git a/code/ZAS/Functions.dm b/code/ZAS - oldbs12/Functions.dm
similarity index 100%
rename from code/ZAS/Functions.dm
rename to code/ZAS - oldbs12/Functions.dm
diff --git a/code/ZAS - oldbs12/Plasma.dm b/code/ZAS - oldbs12/Plasma.dm
new file mode 100644
index 00000000000..716ea216403
--- /dev/null
+++ b/code/ZAS - oldbs12/Plasma.dm
@@ -0,0 +1,163 @@
+var/image/contamination_overlay = image('icons/effects/contamination.dmi')
+
+/pl_control
+ var/PLASMA_DMG = 3
+ var/PLASMA_DMG_NAME = "Plasma Damage Amount"
+ var/PLASMA_DMG_DESC = "Self Descriptive"
+
+ var/CLOTH_CONTAMINATION = 1
+ var/CLOTH_CONTAMINATION_NAME = "Cloth Contamination"
+ var/CLOTH_CONTAMINATION_DESC = "If this is on, plasma does damage by getting into cloth."
+
+ var/PLASMAGUARD_ONLY = 0
+ var/PLASMAGUARD_ONLY_NAME = "\"PlasmaGuard Only\""
+ var/PLASMAGUARD_ONLY_DESC = "If this is on, only biosuits and spacesuits protect against contamination and ill effects."
+
+ var/GENETIC_CORRUPTION = 0
+ var/GENETIC_CORRUPTION_NAME = "Genetic Corruption Chance"
+ var/GENETIC_CORRUPTION_DESC = "Chance of genetic corruption as well as toxic damage, X in 10,000."
+
+ var/SKIN_BURNS = 0
+ var/SKIN_BURNS_DESC = "Plasma has an effect similar to mustard gas on the un-suited."
+ var/SKIN_BURNS_NAME = "Skin Burns"
+
+ var/EYE_BURNS = 1
+ var/EYE_BURNS_NAME = "Eye Burns"
+ var/EYE_BURNS_DESC = "Plasma burns the eyes of anyone not wearing eye protection."
+
+ var/CONTAMINATION_LOSS = 0.02
+ var/CONTAMINATION_LOSS_NAME = "Contamination Loss"
+ var/CONTAMINATION_LOSS_DESC = "How much toxin damage is dealt from contaminated clothing" //Per tick? ASK ARYN
+
+ var/PLASMA_HALLUCINATION = 0
+ var/PLASMA_HALLUCINATION_NAME = "Plasma Hallucination"
+ var/PLASMA_HALLUCINATION_DESC = "Does being in plasma cause you to hallucinate?"
+
+ var/N2O_HALLUCINATION = 1
+ var/N2O_HALLUCINATION_NAME = "N2O Hallucination"
+ var/N2O_HALLUCINATION_DESC = "Does being in sleeping gas cause you to hallucinate?"
+
+
+obj/var/contaminated = 0
+
+
+/obj/item/proc/can_contaminate()
+ //Clothing and backpacks can be contaminated.
+ if(flags & PLASMAGUARD) return 0
+ else if(istype(src,/obj/item/weapon/storage/backpack)) return 0 //Cannot be washed :(
+ else if(istype(src,/obj/item/clothing)) return 1
+
+/obj/item/proc/contaminate()
+ //Do a contamination overlay? Temporary measure to keep contamination less deadly than it was.
+ if(!contaminated)
+ contaminated = 1
+ overlays += contamination_overlay
+
+/obj/item/proc/decontaminate()
+ contaminated = 0
+ overlays -= contamination_overlay
+
+/mob/proc/contaminate()
+
+/mob/living/carbon/human/contaminate()
+ //See if anything can be contaminated.
+
+ if(!pl_suit_protected())
+ suit_contamination()
+
+ if(!pl_head_protected())
+ if(prob(1)) suit_contamination() //Plasma can sometimes get through such an open suit.
+
+//Cannot wash backpacks currently.
+// if(istype(back,/obj/item/weapon/storage/backpack))
+// back.contaminate()
+
+/mob/proc/pl_effects()
+
+/mob/living/carbon/human/pl_effects()
+ //Handles all the bad things plasma can do.
+
+ //Contamination
+ if(vsc.plc.CLOTH_CONTAMINATION) contaminate()
+
+ //Anything else requires them to not be dead.
+ if(stat >= 2)
+ return
+
+ //Burn skin if exposed.
+ if(vsc.plc.SKIN_BURNS)
+ if(!pl_head_protected() || !pl_suit_protected())
+ burn_skin(0.75)
+ if(prob(20)) src << "\red Your skin burns!"
+ updatehealth()
+
+ //Burn eyes if exposed.
+ if(vsc.plc.EYE_BURNS)
+ if(!head)
+ if(!wear_mask)
+ burn_eyes()
+ else
+ if(!(wear_mask.flags & MASKCOVERSEYES))
+ burn_eyes()
+ else
+ if(!(head.flags & HEADCOVERSEYES))
+ if(!wear_mask)
+ burn_eyes()
+ else
+ if(!(wear_mask.flags & MASKCOVERSEYES))
+ burn_eyes()
+
+ //Genetic Corruption
+ if(vsc.plc.GENETIC_CORRUPTION)
+ if(rand(1,10000) < vsc.plc.GENETIC_CORRUPTION)
+ randmutb(src)
+ src << "\red High levels of toxins cause you to spontaneously mutate."
+ domutcheck(src,null)
+
+
+/mob/living/carbon/human/proc/burn_eyes()
+ //The proc that handles eye burning.
+ if(prob(20)) src << "\red Your eyes burn!"
+ var/datum/organ/internal/eyes/E = internal_organs["eyes"]
+ E.damage += 2.5
+ eye_blurry = min(eye_blurry+1.5,50)
+ if (prob(max(0,E.damage - 15) + 1) &&!eye_blind)
+ src << "\red You are blinded!"
+ eye_blind += 20
+
+/mob/living/carbon/human/proc/pl_head_protected()
+ //Checks if the head is adequately sealed.
+ if(head)
+ if(vsc.plc.PLASMAGUARD_ONLY)
+ if(head.flags & PLASMAGUARD)
+ return 1
+ else if(head.flags & HEADCOVERSEYES)
+ return 1
+ return 0
+
+/mob/living/carbon/human/proc/pl_suit_protected()
+ //Checks if the suit is adequately sealed.
+ if(wear_suit)
+ if(vsc.plc.PLASMAGUARD_ONLY)
+ if(wear_suit.flags & PLASMAGUARD) return 1
+ else
+ if(wear_suit.flags_inv & HIDEJUMPSUIT) return 1
+ return 0
+
+/mob/living/carbon/human/proc/suit_contamination()
+ //Runs over the things that can be contaminated and does so.
+ if(w_uniform) w_uniform.contaminate()
+ if(shoes) shoes.contaminate()
+ if(gloves) gloves.contaminate()
+
+
+turf/Entered(obj/item/I)
+ . = ..()
+ //Items that are in plasma, but not on a mob, can still be contaminated.
+ if(istype(I) && vsc.plc.CLOTH_CONTAMINATION)
+ var/datum/gas_mixture/env = return_air(1)
+ if(!env)
+ return
+ if(env.toxins > MOLES_PLASMA_VISIBLE + 1)
+ if(I.can_contaminate())
+ I.contaminate()
\ No newline at end of file
diff --git a/code/ZAS - oldbs12/Variable Settings.dm b/code/ZAS - oldbs12/Variable Settings.dm
new file mode 100644
index 00000000000..92841a02dcd
--- /dev/null
+++ b/code/ZAS - oldbs12/Variable Settings.dm
@@ -0,0 +1,336 @@
+var/global/vs_control/vsc = new
+
+/vs_control
+ var/fire_consuption_rate = 0.25
+ var/fire_consuption_rate_NAME = "Fire - Air Consumption Ratio"
+ var/fire_consuption_rate_DESC = "Ratio of air removed and combusted per tick."
+
+ var/fire_firelevel_multiplier = 25
+ var/fire_firelevel_multiplier_NAME = "Fire - Firelevel Constant"
+ var/fire_firelevel_multiplier_DESC = "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires."
+
+ var/fire_fuel_energy_release = 397000
+ var/fire_fuel_energy_release_NAME = "Fire - Fuel energy release"
+ var/fire_fuel_energy_release_DESC = "The energy in joule released when burning one mol of a burnable substance"
+
+
+ var/IgnitionLevel = 0.5
+ var/IgnitionLevel_DESC = "Determines point at which fire can ignite"
+
+ var/airflow_lightest_pressure = 20
+ var/airflow_lightest_pressure_NAME = "Airflow - Small Movement Threshold %"
+ var/airflow_lightest_pressure_DESC = "Percent of 1 Atm. at which items with the small weight classes will move."
+
+ var/airflow_light_pressure = 35
+ var/airflow_light_pressure_NAME = "Airflow - Medium Movement Threshold %"
+ var/airflow_light_pressure_DESC = "Percent of 1 Atm. at which items with the medium weight classes will move."
+
+ var/airflow_medium_pressure = 50
+ var/airflow_medium_pressure_NAME = "Airflow - Heavy Movement Threshold %"
+ var/airflow_medium_pressure_DESC = "Percent of 1 Atm. at which items with the largest weight classes will move."
+
+ var/airflow_heavy_pressure = 65
+ var/airflow_heavy_pressure_NAME = "Airflow - Mob Movement Threshold %"
+ var/airflow_heavy_pressure_DESC = "Percent of 1 Atm. at which mobs will move."
+
+ var/airflow_dense_pressure = 85
+ var/airflow_dense_pressure_NAME = "Airflow - Dense Movement Threshold %"
+ var/airflow_dense_pressure_DESC = "Percent of 1 Atm. at which items with canisters and closets will move."
+
+ var/airflow_stun_pressure = 60
+ var/airflow_stun_pressure_NAME = "Airflow - Mob Stunning Threshold %"
+ var/airflow_stun_pressure_DESC = "Percent of 1 Atm. at which mobs will be stunned by airflow."
+
+ var/airflow_stun_cooldown = 60
+ var/airflow_stun_cooldown_NAME = "Aiflow Stunning - Cooldown"
+ var/airflow_stun_cooldown_DESC = "How long, in tenths of a second, to wait before stunning them again."
+
+ var/airflow_stun = 1
+ var/airflow_stun_NAME = "Airflow Impact - Stunning"
+ var/airflow_stun_DESC = "How much a mob is stunned when hit by an object."
+
+ var/airflow_damage = 2
+ var/airflow_damage_NAME = "Airflow Impact - Damage"
+ var/airflow_damage_DESC = "Damage from airflow impacts."
+
+ var/airflow_speed_decay = 1.5
+ var/airflow_speed_decay_NAME = "Airflow Speed Decay"
+ var/airflow_speed_decay_DESC = "How rapidly the speed gained from airflow decays."
+
+ var/airflow_delay = 30
+ var/airflow_delay_NAME = "Airflow Retrigger Delay"
+ var/airflow_delay_DESC = "Time in deciseconds before things can be moved by airflow again."
+
+ var/airflow_mob_slowdown = 1
+ var/airflow_mob_slowdown_NAME = "Airflow Slowdown"
+ var/airflow_mob_slowdown_DESC = "Time in tenths of a second to add as a delay to each movement by a mob if they are fighting the pull of the airflow."
+
+ var/connection_insulation = 1
+ var/connection_insulation_NAME = "Connections - Insulation"
+ var/connection_insulation_DESC = "Boolean, should doors forbid heat transfer?"
+
+ var/connection_temperature_delta = 10
+ var/connection_temperature_delta_NAME = "Connections - Temperature Difference"
+ var/connection_temperature_delta_DESC = "The smallest temperature difference which will cause heat to travel through doors."
+
+
+/vs_control/var/list/settings = list()
+/vs_control/var/list/bitflags = list("1","2","4","8","16","32","64","128","256","512","1024")
+/vs_control/var/pl_control/plc = new()
+
+/vs_control/New()
+ . = ..()
+ settings = vars.Copy()
+
+ var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars.
+ for(var/V in D.vars)
+ settings -= V
+
+ for(var/V in settings)
+ if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC") || findtextEx(V,"_METHOD"))
+ settings -= V
+
+ settings -= "settings"
+ settings -= "bitflags"
+ settings -= "plc"
+
+/vs_control/proc/ChangeSettingsDialog(mob/user,list/L)
+ //var/which = input(user,"Choose a setting:") in L
+ var/dat = ""
+ for(var/ch in L)
+ if(findtextEx(ch,"_RANDOM") || findtextEx(ch,"_DESC") || findtextEx(ch,"_METHOD") || findtextEx(ch,"_NAME")) continue
+ var/vw
+ var/vw_desc = "No Description."
+ var/vw_name = ch
+ if(ch in plc.settings)
+ vw = plc.vars[ch]
+ if("[ch]_DESC" in plc.vars) vw_desc = plc.vars["[ch]_DESC"]
+ if("[ch]_NAME" in plc.vars) vw_name = plc.vars["[ch]_NAME"]
+ else
+ vw = vars[ch]
+ if("[ch]_DESC" in vars) vw_desc = vars["[ch]_DESC"]
+ if("[ch]_NAME" in vars) vw_name = vars["[ch]_NAME"]
+ dat += "[vw_name] = [vw] \[Change\]
"
+ dat += "[vw_desc]
"
+ user << browse(dat,"window=settings")
+
+/vs_control/Topic(href,href_list)
+ if("changevar" in href_list)
+ ChangeSetting(usr,href_list["changevar"])
+
+/vs_control/proc/ChangeSetting(mob/user,ch)
+ var/vw
+ var/how = "Text"
+ var/display_description = ch
+ if(ch in plc.settings)
+ vw = plc.vars[ch]
+ if("[ch]_NAME" in plc.vars)
+ display_description = plc.vars["[ch]_NAME"]
+ if("[ch]_METHOD" in plc.vars)
+ how = plc.vars["[ch]_METHOD"]
+ else
+ if(isnum(vw))
+ how = "Numeric"
+ else
+ how = "Text"
+ else
+ vw = vars[ch]
+ if("[ch]_NAME" in vars)
+ display_description = vars["[ch]_NAME"]
+ if("[ch]_METHOD" in vars)
+ how = vars["[ch]_METHOD"]
+ else
+ if(isnum(vw))
+ how = "Numeric"
+ else
+ how = "Text"
+ var/newvar = vw
+ switch(how)
+ if("Numeric")
+ newvar = input(user,"Enter a number:","Settings",newvar) as num
+ if("Bit Flag")
+ var/flag = input(user,"Toggle which bit?","Settings") in bitflags
+ flag = text2num(flag)
+ if(newvar & flag)
+ newvar &= ~flag
+ else
+ newvar |= flag
+ if("Toggle")
+ newvar = !newvar
+ if("Text")
+ newvar = input(user,"Enter a string:","Settings",newvar) as text
+ if("Long Text")
+ newvar = input(user,"Enter text:","Settings",newvar) as message
+ vw = newvar
+ if(ch in plc.settings)
+ plc.vars[ch] = vw
+ else
+ vars[ch] = vw
+ if(how == "Toggle")
+ newvar = (newvar?"ON":"OFF")
+ world << "\blue [key_name(user)] changed the setting [display_description] to [newvar]."
+ if(ch in plc.settings)
+ ChangeSettingsDialog(user,plc.settings)
+ else
+ ChangeSettingsDialog(user,settings)
+
+/vs_control/proc/RandomizeWithProbability()
+ for(var/V in settings)
+ var/newvalue
+ if("[V]_RANDOM" in vars)
+ if(isnum(vars["[V]_RANDOM"]))
+ newvalue = prob(vars["[V]_RANDOM"])
+ else if(istext(vars["[V]_RANDOM"]))
+ newvalue = roll(vars["[V]_RANDOM"])
+ else
+ newvalue = vars[V]
+ V = newvalue
+
+/vs_control/proc/ChangePlasma()
+ for(var/V in plc.settings)
+ plc.Randomize(V)
+
+/vs_control/proc/SetDefault(var/mob/user)
+ var/list/setting_choices = list("Plasma - Standard", "Plasma - Low Hazard", "Plasma - High Hazard", "Plasma - Oh Shit!",\
+ "ZAS - Normal", "ZAS - Forgiving", "ZAS - Dangerous", "ZAS - Hellish")
+ var/def = input(user, "Which of these presets should be used?") as null|anything in setting_choices
+ if(!def)
+ return
+ switch(def)
+ if("Plasma - Standard")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 0
+ plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000.
+ plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 0
+ plc.CONTAMINATION_LOSS = 0.02
+
+ if("Plasma - Low Hazard")
+ plc.CLOTH_CONTAMINATION = 0 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 0
+ plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000
+ plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 0
+ plc.CONTAMINATION_LOSS = 0.01
+
+ if("Plasma - High Hazard")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 0
+ plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000.
+ plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 1
+ plc.CONTAMINATION_LOSS = 0.05
+
+ if("Plasma - Oh Shit!")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 1
+ plc.GENETIC_CORRUPTION = 5 //Chance of genetic corruption as well as toxic damage, X in 1000.
+ plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 1
+ plc.CONTAMINATION_LOSS = 0.075
+
+ if("ZAS - Normal")
+ airflow_lightest_pressure = 20
+ airflow_light_pressure = 35
+ airflow_medium_pressure = 50
+ airflow_heavy_pressure = 65
+ airflow_dense_pressure = 85
+ airflow_stun_pressure = 60
+ airflow_stun_cooldown = 60
+ airflow_stun = 1
+ airflow_damage = 2
+ airflow_speed_decay = 1.5
+ airflow_delay = 30
+ airflow_mob_slowdown = 1
+
+ if("ZAS - Forgiving")
+ airflow_lightest_pressure = 45
+ airflow_light_pressure = 60
+ airflow_medium_pressure = 120
+ airflow_heavy_pressure = 110
+ airflow_dense_pressure = 200
+ airflow_stun_pressure = 150
+ airflow_stun_cooldown = 90
+ airflow_stun = 0.15
+ airflow_damage = 0.15
+ airflow_speed_decay = 1.5
+ airflow_delay = 50
+ airflow_mob_slowdown = 0
+
+ if("ZAS - Dangerous")
+ airflow_lightest_pressure = 15
+ airflow_light_pressure = 30
+ airflow_medium_pressure = 45
+ airflow_heavy_pressure = 55
+ airflow_dense_pressure = 70
+ airflow_stun_pressure = 50
+ airflow_stun_cooldown = 50
+ airflow_stun = 2
+ airflow_damage = 3
+ airflow_speed_decay = 1.2
+ airflow_delay = 25
+ airflow_mob_slowdown = 2
+
+ if("ZAS - Hellish")
+ airflow_lightest_pressure = 20
+ airflow_light_pressure = 30
+ airflow_medium_pressure = 40
+ airflow_heavy_pressure = 50
+ airflow_dense_pressure = 60
+ airflow_stun_pressure = 40
+ airflow_stun_cooldown = 40
+ airflow_stun = 3
+ airflow_damage = 4
+ airflow_speed_decay = 1
+ airflow_delay = 20
+ airflow_mob_slowdown = 3
+ connection_insulation = 0
+
+
+ world << "\blue [key_name(user)] changed the global plasma/ZAS settings to \"[def]\""
+
+/pl_control/var/list/settings = list()
+
+/pl_control/New()
+ . = ..()
+ settings = vars.Copy()
+
+ var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars.
+ for(var/V in D.vars)
+ settings -= V
+
+ for(var/V in settings)
+ if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC"))
+ settings -= V
+
+ settings -= "settings"
+
+/pl_control/proc/Randomize(V)
+ var/newvalue
+ if("[V]_RANDOM" in vars)
+ if(isnum(vars["[V]_RANDOM"]))
+ newvalue = prob(vars["[V]_RANDOM"])
+ else if(istext(vars["[V]_RANDOM"]))
+ var/txt = vars["[V]_RANDOM"]
+ if(findtextEx(txt,"PROB"))
+ txt = text2list(txt,"/")
+ txt[1] = replacetext(txt[1],"PROB","")
+ var/p = text2num(txt[1])
+ var/r = txt[2]
+ if(prob(p))
+ newvalue = roll(r)
+ else
+ newvalue = vars[V]
+ else if(findtextEx(txt,"PICK"))
+ txt = replacetext(txt,"PICK","")
+ txt = text2list(txt,",")
+ newvalue = pick(txt)
+ else
+ newvalue = roll(txt)
+ else
+ newvalue = vars[V]
+ vars[V] = newvalue
diff --git a/code/ZAS/ZAS_Turfs.dm b/code/ZAS - oldbs12/ZAS_Turfs.dm
similarity index 99%
rename from code/ZAS/ZAS_Turfs.dm
rename to code/ZAS - oldbs12/ZAS_Turfs.dm
index 8c3bc55843f..84d3e9450e4 100644
--- a/code/ZAS/ZAS_Turfs.dm
+++ b/code/ZAS - oldbs12/ZAS_Turfs.dm
@@ -118,7 +118,7 @@
. = ..()
-/turf/simulated/Del()
+/turf/simulated/Destroy()
if(active_hotspot)
del(active_hotspot)
if(blocks_air)
diff --git a/code/ZAS/ZAS_Zones.dm b/code/ZAS - oldbs12/ZAS_Zones.dm
similarity index 100%
rename from code/ZAS/ZAS_Zones.dm
rename to code/ZAS - oldbs12/ZAS_Zones.dm
diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm
index efa45700f4e..ea839b87298 100644
--- a/code/ZAS/Airflow.dm
+++ b/code/ZAS/Airflow.dm
@@ -1,53 +1,8 @@
/*
-
-CONTAINS:
-All AirflowX() procs, all Variable Setting Controls for airflow, save/load variable tweaks for airflow.
-
-VARIABLES:
-
-atom/movable/airflow_dest
- The destination turf of a flying object.
-
-atom/movable/airflow_speed
- The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time.
-
-
-OVERLOADABLE PROCS:
-
-mob/airflow_stun()
- Contains checks for and results of being stunned by airflow.
- Called when airflow quantities exceed airflow_medium_pressure.
- RETURNS: Null
-
-atom/movable/check_airflow_movable(n)
- Contains checks for moving any object due to airflow.
- n is the pressure that is flowing.
- RETURNS: 1 if the object moves under the air conditions, 0 if it stays put.
-
-atom/movable/airflow_hit(atom/A)
- Contains results of hitting a solid object (A) due to airflow.
- A is the dense object hit.
- Use airflow_speed to determine how fast the projectile was going.
-
-
-AUTOMATIC PROCS:
-
-Airflow(zone/A, zone/B)
- Causes objects to fly along a pressure gradient.
- Called by zone updates. A and B are two connected zones.
-
-AirflowSpace(zone/A)
- Causes objects to fly into space.
- Called by zone updates. A is a zone connected to space.
-
-atom/movable/GotoAirflowDest(n)
-atom/movable/RepelAirflowDest(n)
- Called by main airflow procs to cause the object to fly to or away from destination at speed n.
- Probably shouldn't call this directly unless you know what you're
- doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle.
-
+Contains helper procs for airflow, handled in /connection_group.
*/
+
mob/var/tmp/last_airflow_stun = 0
mob/proc/airflow_stun()
if(stat == 2)
@@ -70,7 +25,7 @@ mob/living/carbon/human/airflow_stun()
if(last_airflow_stun > world.time - vsc.airflow_stun_cooldown) return 0
if(buckled) return 0
if(shoes)
- if((shoes.flags & NOSLIP) || (species.bodyflags & FEET_NOSLIP)) return 0
+ if(shoes.flags & NOSLIP) return 0
if(!(status_flags & CANSTUN) && !(status_flags & CANWEAKEN))
src << "\blue You stay upright as the air rushes past you."
return 0
@@ -108,124 +63,6 @@ obj/item/check_airflow_movable(n)
if(4,5)
if(n < vsc.airflow_medium_pressure) return 0
-//The main airflow code. Called by zone updates.
-//Zones A and B are air zones. n represents the amount of air moved.
-
-proc/Airflow(zone/A, zone/B)
-
- var/n = B.air.return_pressure() - A.air.return_pressure()
-
- //Don't go any further if n is lower than the lowest value needed for airflow.
- if(abs(n) < vsc.airflow_lightest_pressure) return
-
- //These turfs are the midway point between A and B, and will be the destination point for thrown objects.
- var/list/connection/connections_A = A.connections
- var/list/turf/connected_turfs = list()
- for(var/connection/C in connections_A) //Grab the turf that is in the zone we are flowing to (determined by n)
- if( ( A == C.A.zone || A == C.zone_A ) && ( B == C.B.zone || B == C.zone_B ) )
- if(n < 0)
- connected_turfs |= C.B
- else
- connected_turfs |= C.A
- else if( ( A == C.B.zone || A == C.zone_B ) && ( B == C.A.zone || B == C.zone_A ) )
- if(n < 0)
- connected_turfs |= C.A
- else
- connected_turfs |= C.B
-
- //Get lists of things that can be thrown across the room for each zone (assumes air is moving from zone B to zone A)
- var/list/air_sucked = B.movables()
- var/list/air_repelled = A.movables()
- if(n < 0)
- //air is moving from zone A to zone B
- var/list/temporary_pplz = air_sucked
- air_sucked = air_repelled
- air_repelled = temporary_pplz
-
- for(var/atom/movable/M in air_sucked)
-
- if(M.last_airflow > world.time - vsc.airflow_delay) continue
-
- //Check for knocking people over
- if(ismob(M) && n > vsc.airflow_stun_pressure)
- if(M:status_flags & GODMODE) continue
- M:airflow_stun()
-
- if(M.check_airflow_movable(n))
-
- //Check for things that are in range of the midpoint turfs.
- var/list/close_turfs = list()
- for(var/turf/U in connected_turfs)
- if(M in range(U)) close_turfs += U
- if(!close_turfs.len) continue
-
- //If they're already being tossed, don't do it again.
- if(!M.airflow_speed)
-
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
-
- spawn M.GotoAirflowDest(abs(n)/5)
-
- //Do it again for the stuff in the other zone, making it fly away.
- for(var/atom/movable/M in air_repelled)
-
- if(M.last_airflow > world.time - vsc.airflow_delay) continue
-
- if(ismob(M) && abs(n) > vsc.airflow_medium_pressure)
- if(M:status_flags & GODMODE) continue
- M:airflow_stun()
-
- if(M.check_airflow_movable(abs(n)))
-
- var/list/close_turfs = list()
- for(var/turf/U in connected_turfs)
- if(M in range(U)) close_turfs += U
- if(!close_turfs.len) continue
-
- //If they're already being tossed, don't do it again.
- if(!M.airflow_speed)
-
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
-
- spawn M.RepelAirflowDest(abs(n)/5)
-
-proc/AirflowSpace(zone/A)
-
- //The space version of the Airflow(A,B,n) proc.
-
- var/n = A.air.return_pressure()
- //Here, n is determined by only the pressure in the room.
-
- if(n < vsc.airflow_lightest_pressure) return
-
- var/list/connected_turfs = A.unsimulated_tiles //The midpoints are now all the space connections.
- var/list/pplz = A.movables() //We only need to worry about things in the zone, not things in space.
-
- for(var/atom/movable/M in pplz)
-
- if(M.last_airflow > world.time - vsc.airflow_delay) continue
-
- if(ismob(M) && n > vsc.airflow_stun_pressure)
- var/mob/O = M
- if(O.status_flags & GODMODE) continue
- O.airflow_stun()
-
- if(M.check_airflow_movable(n))
-
- var/list/close_turfs = list()
- for(var/turf/U in connected_turfs)
- if(M in range(U)) close_turfs += U
- if(!close_turfs.len) continue
-
- //If they're already being tossed, don't do it again.
- if(!M.airflow_speed)
-
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
- spawn
- if(M) M.GotoAirflowDest(n/10)
- //Sometimes shit breaks, and M isn't there after the spawn.
-
-
/atom/movable/var/tmp/turf/airflow_dest
/atom/movable/var/tmp/airflow_speed = 0
/atom/movable/var/tmp/airflow_time = 0
@@ -414,7 +251,6 @@ zone/proc/movables()
. = list()
for(var/turf/T in contents)
for(var/atom/A in T)
-// if(istype(A, /obj/effect) || istype(A, /mob/aiEye))
if(istype(A, /obj/effect) || istype(A, /mob/camera/aiEye))
continue
- . += A
+ . += A
\ No newline at end of file
diff --git a/code/ZAS/Atom.dm b/code/ZAS/Atom.dm
new file mode 100644
index 00000000000..b4d1de20af4
--- /dev/null
+++ b/code/ZAS/Atom.dm
@@ -0,0 +1,66 @@
+
+
+/atom/var/pressure_resistance = ONE_ATMOSPHERE
+
+atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
+ //Purpose: Determines if the object (or airflow) can pass this atom.
+ //Called by: Movement, airflow.
+ //Inputs: The moving atom (optional), target turf, "height" and air group
+ //Outputs: Boolean if can pass.
+
+ return (!density || !height || air_group)
+
+/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
+ if(!target) return 0
+
+ if(istype(mover)) // turf/Enter(...) will perform more advanced checks
+ return !density
+
+ else // Now, doing more detailed checks for air movement and air group formation
+ if(target.blocks_air||blocks_air)
+ return 0
+
+ for(var/obj/obstacle in src)
+ if(!obstacle.CanPass(mover, target, height, air_group))
+ return 0
+ if(target != src)
+ for(var/obj/obstacle in target)
+ if(!obstacle.CanPass(mover, src, height, air_group))
+ return 0
+
+ return 1
+
+//Basically another way of calling CanPass(null, other, 0, 0) and CanPass(null, other, 1.5, 1).
+//Returns:
+// 0 - Not blocked
+// AIR_BLOCKED - Blocked
+// ZONE_BLOCKED - Not blocked, but zone boundaries will not cross.
+// BLOCKED - Blocked, zone boundaries will not cross even if opened.
+atom/proc/c_airblock(turf/other)
+ #ifdef ZASDBG
+ ASSERT(isturf(other))
+ #endif
+ return !CanPass(null, other, 0, 0) + 2*!CanPass(null, other, 1.5, 1)
+
+
+turf/c_airblock(turf/other)
+ #ifdef ZASDBG
+ ASSERT(isturf(other))
+ #endif
+ if(blocks_air)
+ return BLOCKED
+
+ //Z-level handling code. Always block if there isn't an open space.
+ #ifdef ZLEVELS
+ if(other.z != src.z)
+ if(other.z < src.z)
+ if(!istype(src, /turf/simulated/floor/open)) return BLOCKED
+ else
+ if(!istype(other, /turf/simulated/floor/open)) return BLOCKED
+ #endif
+
+ var/result = 0
+ for(var/atom/movable/M in contents)
+ result |= M.c_airblock(other)
+ if(result == BLOCKED) return BLOCKED
+ return result
\ No newline at end of file
diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm
index 1bb8f9ca54d..b9cf2278a18 100644
--- a/code/ZAS/Connection.dm
+++ b/code/ZAS/Connection.dm
@@ -1,448 +1,165 @@
+#define CONNECTION_DIRECT 2
+#define CONNECTION_SPACE 4
+#define CONNECTION_INVALID 8
+
/*
-This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked.
-Indirect connections will not merge the two zones after they reach equilibrium.
+
+Overview:
+ Connections are made between turfs by air_master.connect(). They represent a single point where two zones converge.
+
+Class Vars:
+ A - Always a simulated turf.
+ B - A simulated or unsimulated turf.
+
+ zoneA - The archived zone of A. Used to check that the zone hasn't changed.
+ zoneB - The archived zone of B. May be null in case of unsimulated connections.
+
+ edge - Stores the edge this connection is in. Can reference an edge that is no longer processed
+ after this connection is removed, so make sure to check edge.coefficient > 0 before re-adding it.
+
+Class Procs:
+
+ mark_direct()
+ Marks this connection as direct. Does not update the edge.
+ Called when the connection is made and there are no doors between A and B.
+ Also called by update() as a correction.
+
+ mark_indirect()
+ Unmarks this connection as direct. Does not update the edge.
+ Called by update() as a correction.
+
+ mark_space()
+ Marks this connection as unsimulated. Updating the connection will check the validity of this.
+ Called when the connection is made.
+ This will not be called as a correction, any connections failing a check against this mark are erased and rebuilt.
+
+ direct()
+ Returns 1 if no doors are in between A and B.
+
+ valid()
+ Returns 1 if the connection has not been erased.
+
+ erase()
+ Called by update() and connection_manager/erase_all().
+ Marks the connection as erased and removes it from its edge.
+
+ update()
+ Called by connection_manager/update_all().
+ Makes numerous checks to decide whether the connection is still valid. Erases it automatically if not.
+
*/
-#define CONNECTION_DIRECT 2
-#define CONNECTION_INDIRECT 1
-#define CONNECTION_CLOSED 0
-/connection
- var/turf/simulated/A
- var/turf/simulated/B
+/connection/var/turf/simulated/A
+/connection/var/turf/simulated/B
+/connection/var/zone/zoneA
+/connection/var/zone/zoneB
- var/zone/zone_A
- var/zone/zone_B
+/connection/var/connection_edge/edge
- var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join.
-
-
-/connection/New(turf/T,turf/O)
- . = ..()
-
- A = T
- B = O
-
- if(A.zone && B.zone)
- if(!A.zone.connections)
- A.zone.connections = list()
- A.zone.connections += src
- zone_A = A.zone
-
- if(!B.zone.connections)
- B.zone.connections = list()
- B.zone.connections += src
- zone_B = B.zone
-
- if(A in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[A]
- connections.Add(src)
- else
- air_master.turfs_with_connections[A] = list(src)
-
- if(B in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[B]
- connections.Add(src)
- else
- air_master.turfs_with_connections[B] = list(src)
-
- if(A.CanPass(null, B, 0, 0))
-
- if(!A.CanPass(null, B, 1.5, 1))
- indirect = CONNECTION_INDIRECT
-
- ConnectZones(A.zone, B.zone, indirect)
-
- else
- ConnectZones(A.zone, B.zone)
- indirect = CONNECTION_CLOSED
+/connection/var/state = 0
+/connection/New(turf/simulated/A, turf/simulated/B)
+ #ifdef ZASDBG
+ ASSERT(air_master.has_valid_zone(A))
+ //ASSERT(air_master.has_valid_zone(B))
+ #endif
+ src.A = A
+ src.B = B
+ zoneA = A.zone
+ if(!istype(B))
+ mark_space()
+ edge = air_master.get_edge(A.zone,B)
+ edge.add_connection(src)
else
- world.log << "Attempted to create connection object for non-zone tiles: [T] ([T.x],[T.y],[T.z]) -> [O] ([O.x],[O.y],[O.z])"
- SoftDelete()
+ zoneB = B.zone
+ edge = air_master.get_edge(A.zone,B.zone)
+ edge.add_connection(src)
+/connection/proc/mark_direct()
+ state |= CONNECTION_DIRECT
+ //world << "Marked direct."
-/connection/Del()
- //remove connections from master lists.
- if(B in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[B]
- connections.Remove(src)
+/connection/proc/mark_indirect()
+ state &= ~CONNECTION_DIRECT
+ //world << "Marked indirect."
- if(A in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[A]
- connections.Remove(src)
+/connection/proc/mark_space()
+ state |= CONNECTION_SPACE
- //Remove connection from zones.
- if(A)
- if(A.zone && A.zone.connections)
- A.zone.connections.Remove(src)
- if(!A.zone.connections.len)
- A.zone.connections = null
+/connection/proc/direct()
+ return (state & CONNECTION_DIRECT)
- if(istype(zone_A) && (!A || A.zone != zone_A))
- if(zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
+/connection/proc/valid()
+ return !(state & CONNECTION_INVALID)
- if(B)
- if(B.zone && B.zone.connections)
- B.zone.connections.Remove(src)
- if(!B.zone.connections.len)
- B.zone.connections = null
+/connection/proc/erase()
+ edge.remove_connection(src)
+ state |= CONNECTION_INVALID
+ //world << "Connection Erased: [state]"
- if(istype(zone_B) && (!B || B.zone != zone_B))
- if(zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- //Disconnect zones while handling unusual conditions.
- // e.g. loss of a zone on a turf
- DisconnectZones(zone_A, zone_B)
-
- //Finally, preform actual deletion.
- . = ..()
-
-
-/connection/proc/SoftDelete()
- //remove connections from master lists.
- if(B in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[B]
- connections.Remove(src)
-
- if(A in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[A]
- connections.Remove(src)
-
- //Remove connection from zones.
- if(A)
- if(A.zone && A.zone.connections)
- A.zone.connections.Remove(src)
- if(!A.zone.connections.len)
- A.zone.connections = null
-
- if(istype(zone_A) && (!A || A.zone != zone_A))
- if(zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
-
- if(B)
- if(B.zone && B.zone.connections)
- B.zone.connections.Remove(src)
- if(!B.zone.connections.len)
- B.zone.connections = null
-
- if(istype(zone_B) && (!B || B.zone != zone_B))
- if(zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- //Disconnect zones while handling unusual conditions.
- // e.g. loss of a zone on a turf
- DisconnectZones(zone_A, zone_B)
-
-
-/connection/proc/ConnectZones(var/zone/zone_1, var/zone/zone_2, open = 0)
-
- //Sanity checking
- if(!istype(zone_1) || !istype(zone_2))
+/connection/proc/update()
+ //world << "Updated, \..."
+ if(!istype(A,/turf/simulated))
+ //world << "Invalid A."
+ erase()
return
- //Handle zones connecting indirectly/directly.
- if(open)
-
- //Create the lists if necessary.
- if(!zone_1.connected_zones)
- zone_1.connected_zones = list()
-
- if(!zone_2.connected_zones)
- zone_2.connected_zones = list()
-
- //Increase the number of connections between zones.
- if(zone_2 in zone_1.connected_zones)
- zone_1.connected_zones[zone_2]++
- else
- zone_1.connected_zones += zone_2
- zone_1.connected_zones[zone_2] = 1
-
- if(zone_1 in zone_2.connected_zones)
- zone_2.connected_zones[zone_1]++
- else
- zone_2.connected_zones += zone_1
- zone_2.connected_zones[zone_1] = 1
-
- if(open == CONNECTION_DIRECT)
- if(!zone_1.direct_connections)
- zone_1.direct_connections = list(src)
- else
- zone_1.direct_connections += src
-
- if(!zone_2.direct_connections)
- zone_2.direct_connections = list(src)
- else
- zone_2.direct_connections += src
-
-
- //Handle closed connections.
- else
-
- //Create the lists
- if(!zone_1.closed_connection_zones)
- zone_1.closed_connection_zones = list()
-
- if(!zone_2.closed_connection_zones)
- zone_2.closed_connection_zones = list()
-
- //Increment the connections.
- if(zone_2 in zone_1.closed_connection_zones)
- zone_1.closed_connection_zones[zone_2]++
- else
- zone_1.closed_connection_zones += zone_2
- zone_1.closed_connection_zones[zone_2] = 1
-
- if(zone_1 in zone_2.closed_connection_zones)
- zone_2.closed_connection_zones[zone_1]++
- else
- zone_2.closed_connection_zones += zone_1
- zone_2.closed_connection_zones[zone_1] = 1
-
- if(zone_1.status == ZONE_SLEEPING)
- zone_1.SetStatus(ZONE_ACTIVE)
-
- if(zone_2.status == ZONE_SLEEPING)
- zone_2.SetStatus(ZONE_ACTIVE)
-
-/connection/proc/DisconnectZones(var/zone/zone_1, var/zone/zone_2)
- //Sanity checking
- if(!istype(zone_1) || !istype(zone_2))
+ var/block_status = air_master.air_blocked(A,B)
+ if(block_status & AIR_BLOCKED)
+ //world << "Blocked connection."
+ erase()
return
-
- if(indirect != CONNECTION_CLOSED)
- //Handle disconnection of indirectly or directly connected zones.
- if( (zone_1 in zone_2.connected_zones) || (zone_2 in zone_1.connected_zones) )
-
- //If there are more than one connection, decrement the number of connections
- //Otherwise, remove all connections between the zones.
- if(zone_2 in zone_1.connected_zones)
- if(zone_1.connected_zones[zone_2] > 1)
- zone_1.connected_zones[zone_2]--
- else
- zone_1.connected_zones -= zone_2
- //remove the list if it is empty
- if(!zone_1.connected_zones.len)
- zone_1.connected_zones = null
-
- //Then do the same for the other zone.
- if(zone_1 in zone_2.connected_zones)
- if(zone_2.connected_zones[zone_1] > 1)
- zone_2.connected_zones[zone_1]--
- else
- zone_2.connected_zones -= zone_1
- if(!zone_2.connected_zones.len)
- zone_2.connected_zones = null
-
- if(indirect == CONNECTION_DIRECT)
- zone_1.direct_connections -= src
- if(!zone_1.direct_connections.len)
- zone_1.direct_connections = null
-
- zone_2.direct_connections -= src
- if(!zone_2.direct_connections.len)
- zone_2.direct_connections = null
-
- else
- //Handle disconnection of closed zones.
- if( (zone_1 in zone_2.closed_connection_zones) || (zone_2 in zone_1.closed_connection_zones) )
-
- //If there are more than one connection, decrement the number of connections
- //Otherwise, remove all connections between the zones.
- if(zone_2 in zone_1.closed_connection_zones)
- if(zone_1.closed_connection_zones[zone_2] > 1)
- zone_1.closed_connection_zones[zone_2]--
- else
- zone_1.closed_connection_zones -= zone_2
- //remove the list if it is empty
- if(!zone_1.closed_connection_zones.len)
- zone_1.closed_connection_zones = null
-
- //Then do the same for the other zone.
- if(zone_1 in zone_2.closed_connection_zones)
- if(zone_2.closed_connection_zones[zone_1] > 1)
- zone_2.closed_connection_zones[zone_1]--
- else
- zone_2.closed_connection_zones -= zone_1
- if(!zone_2.closed_connection_zones.len)
- zone_2.closed_connection_zones = null
-
-
-/connection/proc/Cleanup()
-
- //Check sanity: existance of turfs
- if(!A || !B)
- SoftDelete()
- return
-
- //Check sanity: loss of zone
- if(!A.zone || !B.zone)
- SoftDelete()
- return
-
- //Check sanity: zones are different
- if(A.zone == B.zone)
- SoftDelete()
- return
-
- //Handle zones changing on a turf.
- if((A.zone && A.zone != zone_A) || (B.zone && B.zone != zone_B))
- Sanitize()
-
- if(A.zone && B.zone)
-
- //If no walls are blocking us...
- if(A.ZAirPass(B))
- //...we check to see if there is a door in the way...
- var/door_pass = A.CanPass(null,B,1.5,1)
- //...and if it is opened.
- if(door_pass || A.CanPass(null,B,0,0))
-
- //Make and remove connections to let air pass.
- if(indirect == CONNECTION_CLOSED)
- DisconnectZones(A.zone, B.zone)
- ConnectZones(A.zone, B.zone, door_pass + 1)
-
- if(door_pass)
- indirect = CONNECTION_DIRECT
- else if(!door_pass)
- indirect = CONNECTION_INDIRECT
-
- //The door is instead closed.
- else if(indirect > CONNECTION_CLOSED)
- DisconnectZones(A.zone, B.zone)
- indirect = CONNECTION_CLOSED
- ConnectZones(A.zone, B.zone)
-
- //If I can no longer pass air, better delete
+ else if(block_status & ZONE_BLOCKED)
+ if(direct())
+ mark_indirect()
else
- SoftDelete()
+ mark_direct()
+
+ var/b_is_space = !istype(B,/turf/simulated)
+
+ if(state & CONNECTION_SPACE)
+ if(!b_is_space)
+ //world << "Invalid B."
+ erase()
return
-
-/connection/proc/Sanitize()
- //If the zones change on connected turfs, update it.
-
- //Both zones changed (wat)
- if(A.zone && A.zone != zone_A && B.zone && B.zone != zone_B)
-
- //If the zones have gotten swapped
- // (do not ask me how, I am just being anal retentive about sanity)
- if(A.zone == zone_B && B.zone == zone_A)
- var/turf/temp = B
- B = A
- A = temp
- zone_B = B.zone
- zone_A = A.zone
- return
-
- //Handle removal of connections from archived zones.
- if(zone_A && zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
-
- if(zone_B && zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- if(A.zone)
- if(!A.zone.connections)
- A.zone.connections = list()
- A.zone.connections |= src
-
- if(B.zone)
- if(!B.zone.connections)
- B.zone.connections = list()
- B.zone.connections |= src
-
- //If either zone is null, we disconnect the archived ones after cleaning up the connections.
- if(!A.zone || !B.zone)
- if(zone_A && zone_B)
- DisconnectZones(zone_B, zone_A)
-
+ if(A.zone != zoneA)
+ //world << "Zone changed, \..."
if(!A.zone)
- zone_A = A.zone
+ erase()
+ //world << "erased."
+ return
+ else
+ edge.remove_connection(src)
+ edge = air_master.get_edge(A.zone, B)
+ edge.add_connection(src)
+ zoneA = A.zone
- if(!B.zone)
- zone_B = B.zone
+ //world << "valid."
+ return
+
+ else if(b_is_space)
+ //world << "Invalid B."
+ erase()
+ return
+
+ if(A.zone == B.zone)
+ //world << "A == B"
+ erase()
+ return
+
+ if(A.zone != zoneA || (zoneB && (B.zone != zoneB)))
+
+ //world << "Zones changed, \..."
+ if(A.zone && B.zone)
+ edge.remove_connection(src)
+ edge = air_master.get_edge(A.zone, B.zone)
+ edge.add_connection(src)
+ zoneA = A.zone
+ zoneB = B.zone
+ else
+ //world << "erased."
+ erase()
return
- //Handle diconnection and reconnection of zones.
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- ConnectZones(A.zone, B.zone, indirect)
- //resetting values of archived values.
- zone_B = B.zone
- zone_A = A.zone
-
- //The "A" zone changed.
- else if(A.zone && A.zone != zone_A)
-
- //Handle connection cleanup
- if(zone_A)
- if(zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
-
- if(A.zone)
- if(!A.zone.connections)
- A.zone.connections = list()
- A.zone.connections |= src
-
- //If the "A" zone is null, we disconnect the archived ones after cleaning up the connections.
- if(!A.zone)
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- zone_A = A.zone
- return
-
- //Handle diconnection and reconnection of zones.
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- ConnectZones(A.zone, B.zone, indirect)
- zone_A = A.zone
-
- //The "B" zone changed.
- else if(B.zone && B.zone != zone_B)
-
- //Handle connection cleanup
- if(zone_B)
- if(zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- if(B.zone)
- if(!B.zone.connections)
- B.zone.connections = list()
- B.zone.connections |= src
-
- //If the "B" zone is null, we disconnect the archived ones after cleaning up the connections.
- if(!B.zone)
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- zone_B = B.zone
- return
-
- //Handle diconnection and reconnection of zones.
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- ConnectZones(A.zone, B.zone, indirect)
- zone_B = B.zone
-
-
-#undef CONNECTION_DIRECT
-#undef CONNECTION_INDIRECT
-#undef CONNECTION_CLOSED
+ //world << "valid."
\ No newline at end of file
diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm
new file mode 100644
index 00000000000..c31dbf9a034
--- /dev/null
+++ b/code/ZAS/ConnectionGroup.dm
@@ -0,0 +1,427 @@
+/*
+
+Overview:
+ These are what handle gas transfers between zones and into space.
+ They are found in a zone's edges list and in air_master.edges.
+ Each edge updates every air tick due to their role in gas transfer.
+ They come in two flavors, /connection_edge/zone and /connection_edge/unsimulated.
+ As the type names might suggest, they handle inter-zone and spacelike connections respectively.
+
+Class Vars:
+
+ A - This always holds a zone. In unsimulated edges, it holds the only zone.
+
+ connecting_turfs - This holds a list of connected turfs, mainly for the sake of airflow.
+
+ coefficent - This is a marker for how many connections are on this edge. Used to determine the ratio of flow.
+
+ connection_edge/zone
+
+ B - This holds the second zone with which the first zone equalizes.
+
+ direct - This counts the number of direct (i.e. with no doors) connections on this edge.
+ Any value of this is sufficient to make the zones mergeable.
+
+ connection_edge/unsimulated
+
+ B - This holds an unsimulated turf which has the gas values this edge is mimicing.
+
+ air - Retrieved from B on creation and used as an argument for the legacy ShareSpace() proc.
+
+Class Procs:
+
+ add_connection(connection/c)
+ Adds a connection to this edge. Usually increments the coefficient and adds a turf to connecting_turfs.
+
+ remove_connection(connection/c)
+ Removes a connection from this edge. This works even if c is not in the edge, so be careful.
+ If the coefficient reaches zero as a result, the edge is erased.
+
+ contains_zone(zone/Z)
+ Returns true if either A or B is equal to Z. Unsimulated connections return true only on A.
+
+ erase()
+ Removes this connection from processing and zone edge lists.
+
+ tick()
+ Called every air tick on edges in the processing list. Equalizes gas.
+
+ flow(list/movable, differential, repelled)
+ Airflow proc causing all objects in movable to be checked against a pressure differential.
+ If repelled is true, the objects move away from any turf in connecting_turfs, otherwise they approach.
+ A check against vsc.lightest_airflow_pressure should generally be performed before calling this.
+
+ get_connected_zone(zone/from)
+ Helper proc that allows getting the other zone of an edge given one of them.
+ Only on /connection_edge/zone, otherwise use A.
+
+*/
+
+
+/connection_edge/var/zone/A
+
+/connection_edge/var/list/connecting_turfs = list()
+
+/connection_edge/var/coefficient = 0
+
+/connection_edge/New()
+ CRASH("Cannot make connection edge without specifications.")
+
+/connection_edge/proc/add_connection(connection/c)
+ coefficient++
+ //world << "Connection added: [type] Coefficient: [coefficient]"
+
+/connection_edge/proc/remove_connection(connection/c)
+ //world << "Connection removed: [type] Coefficient: [coefficient-1]"
+ coefficient--
+ if(coefficient <= 0)
+ erase()
+
+/connection_edge/proc/contains_zone(zone/Z)
+
+/connection_edge/proc/erase()
+ air_master.remove_edge(src)
+ //world << "[type] Erased."
+
+/connection_edge/proc/tick()
+
+/connection_edge/proc/flow(list/movable, differential, repelled)
+ for(var/atom/movable/M in movable)
+
+ //If they're already being tossed, don't do it again.
+ if(M.last_airflow > world.time - vsc.airflow_delay) continue
+ if(M.airflow_speed) continue
+
+ //Check for knocking people over
+ if(ismob(M) && differential > vsc.airflow_stun_pressure)
+ if(M:status_flags & GODMODE) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(differential))
+ //Check for things that are in range of the midpoint turfs.
+ var/list/close_turfs = list()
+ for(var/turf/U in connecting_turfs)
+ if(get_dist(M,U) < world.view) close_turfs += U
+ if(!close_turfs.len) continue
+
+ M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+
+ if(repelled) spawn if(M) M.RepelAirflowDest(differential/5)
+ else spawn if(M) M.GotoAirflowDest(differential/10)
+
+
+
+
+/connection_edge/zone/var/zone/B
+/connection_edge/zone/var/direct = 0
+
+/connection_edge/zone/New(zone/A, zone/B)
+
+ src.A = A
+ src.B = B
+ A.edges.Add(src)
+ B.edges.Add(src)
+ //id = edge_id(A,B)
+ //world << "New edge between [A] and [B]"
+
+/connection_edge/zone/add_connection(connection/c)
+ . = ..()
+ connecting_turfs.Add(c.A)
+ if(c.direct()) direct++
+
+/connection_edge/zone/remove_connection(connection/c)
+ connecting_turfs.Remove(c.A)
+ if(c.direct()) direct--
+ . = ..()
+
+/connection_edge/zone/contains_zone(zone/Z)
+ return A == Z || B == Z
+
+/connection_edge/zone/erase()
+ A.edges.Remove(src)
+ B.edges.Remove(src)
+ . = ..()
+
+/connection_edge/zone/tick()
+ if(A.invalid || B.invalid)
+ erase()
+ return
+ //world << "[id]: Tick [air_master.current_cycle]: \..."
+ if(direct)
+ if(air_master.equivalent_pressure(A, B))
+ //world << "merged."
+ erase()
+ air_master.merge(A, B)
+ //world << "zones merged."
+ return
+
+ //air_master.equalize(A, B)
+ ShareRatio(A.air,B.air,coefficient)
+ air_master.mark_zone_update(A)
+ air_master.mark_zone_update(B)
+ //world << "equalized."
+
+ var/differential = A.air.return_pressure() - B.air.return_pressure()
+ if(abs(differential) < vsc.airflow_lightest_pressure) return
+
+ var/list/attracted
+ var/list/repelled
+ if(differential > 0)
+ attracted = A.movables()
+ repelled = B.movables()
+ else
+ attracted = B.movables()
+ repelled = A.movables()
+
+ flow(attracted, abs(differential), 0)
+ flow(repelled, abs(differential), 1)
+
+//Helper proc to get connections for a zone.
+/connection_edge/zone/proc/get_connected_zone(zone/from)
+ if(A == from) return B
+ else return A
+
+/connection_edge/unsimulated/var/turf/B
+/connection_edge/unsimulated/var/datum/gas_mixture/air
+
+/connection_edge/unsimulated/New(zone/A, turf/B)
+ src.A = A
+ src.B = B
+ A.edges.Add(src)
+ air = B.return_air()
+ //id = 52*A.id
+ //world << "New edge from [A] to [B]."
+
+/connection_edge/unsimulated/add_connection(connection/c)
+ . = ..()
+ connecting_turfs.Add(c.B)
+ air.group_multiplier = coefficient
+
+/connection_edge/unsimulated/remove_connection(connection/c)
+ connecting_turfs.Remove(c.B)
+ air.group_multiplier = coefficient
+ . = ..()
+
+/connection_edge/unsimulated/erase()
+ A.edges.Remove(src)
+ . = ..()
+
+/connection_edge/unsimulated/contains_zone(zone/Z)
+ return A == Z
+
+/connection_edge/unsimulated/tick()
+ if(A.invalid)
+ erase()
+ return
+ //world << "[id]: Tick [air_master.current_cycle]: To [B]!"
+ //A.air.mimic(B, coefficient)
+ ShareSpace(A.air,air,dbg_out)
+ air_master.mark_zone_update(A)
+
+ var/differential = A.air.return_pressure() - air.return_pressure()
+ if(abs(differential) < vsc.airflow_lightest_pressure) return
+
+ var/list/attracted = A.movables()
+ flow(attracted, abs(differential), differential < 0)
+
+var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
+
+proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
+ //Shares a specific ratio of gas between mixtures using simple weighted averages.
+ var
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ ratio = sharing_lookup_table[6]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ size = max(1,A.group_multiplier)
+ share_size = max(1,B.group_multiplier)
+
+ full_oxy = A.oxygen * size
+ full_nitro = A.nitrogen * size
+ full_co2 = A.carbon_dioxide * size
+ full_plasma = A.toxins * size
+
+ full_heat_capacity = A.heat_capacity() * size
+
+ s_full_oxy = B.oxygen * share_size
+ s_full_nitro = B.nitrogen * share_size
+ s_full_co2 = B.carbon_dioxide * share_size
+ s_full_plasma = B.toxins * share_size
+
+ s_full_heat_capacity = B.heat_capacity() * share_size
+
+ oxy_avg = (full_oxy + s_full_oxy) / (size + share_size)
+ nit_avg = (full_nitro + s_full_nitro) / (size + share_size)
+ co2_avg = (full_co2 + s_full_co2) / (size + share_size)
+ plasma_avg = (full_plasma + s_full_plasma) / (size + share_size)
+
+ temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
+
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[connecting_tiles]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
+ A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg )
+ A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
+ A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg )
+
+ A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg )
+
+ B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
+ B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg )
+ B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
+ B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg )
+
+ B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg )
+
+ for(var/datum/gas/G in A.trace_gases)
+ var/datum/gas/H = locate(G.type) in B.trace_gases
+ if(H)
+ var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+ else
+ H = new G.type
+ B.trace_gases += H
+ var/G_avg = (G.moles*size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+
+ for(var/datum/gas/G in B.trace_gases)
+ var/datum/gas/H = locate(G.type) in A.trace_gases
+ if(!H)
+ H = new G.type
+ A.trace_gases += H
+ var/G_avg = (G.moles*size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+
+ A.update_values()
+ B.update_values()
+
+ if(A.compare(B)) return 1
+ else return 0
+
+proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
+ //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room.
+ if(!unsimulated_tiles)
+ return 0
+
+ var
+ unsim_oxygen = 0
+ unsim_nitrogen = 0
+ unsim_co2 = 0
+ unsim_plasma = 0
+ unsim_heat_capacity = 0
+ unsim_temperature = 0
+
+ size = max(1,A.group_multiplier)
+
+ var/tileslen
+ var/share_size
+
+ if(istype(unsimulated_tiles, /datum/gas_mixture))
+ var/datum/gas_mixture/avg_unsim = unsimulated_tiles
+ unsim_oxygen = avg_unsim.oxygen
+ unsim_co2 = avg_unsim.carbon_dioxide
+ unsim_nitrogen = avg_unsim.nitrogen
+ unsim_plasma = avg_unsim.toxins
+ unsim_temperature = avg_unsim.temperature
+ share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier)
+ tileslen = avg_unsim.group_multiplier
+
+ if(dbg_output)
+ world << "O2: [unsim_oxygen] N2: [unsim_nitrogen] Size: [share_size] Tiles: [tileslen]"
+
+ else if(istype(unsimulated_tiles, /list))
+ if(!unsimulated_tiles.len)
+ return 0
+ // We use the same size for the potentially single space tile
+ // as we use for the entire room. Why is this?
+ // Short answer: We do not want larger rooms to depressurize more
+ // slowly than small rooms, preserving our good old "hollywood-style"
+ // oh-shit effect when large rooms get breached, but still having small
+ // rooms remain pressurized for long enough to make escape possible.
+ share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
+ var/correction_ratio = share_size / unsimulated_tiles.len
+
+ for(var/turf/T in unsimulated_tiles)
+ unsim_oxygen += T.oxygen
+ unsim_co2 += T.carbon_dioxide
+ unsim_nitrogen += T.nitrogen
+ unsim_plasma += T.toxins
+ unsim_temperature += T.temperature/unsimulated_tiles.len
+
+ //These values require adjustment in order to properly represent a room of the specified size.
+ unsim_oxygen *= correction_ratio
+ unsim_co2 *= correction_ratio
+ unsim_nitrogen *= correction_ratio
+ unsim_plasma *= correction_ratio
+ tileslen = unsimulated_tiles.len
+
+ else //invalid input type
+ return 0
+
+ unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma)
+
+ var
+ ratio = sharing_lookup_table[6]
+
+ old_pressure = A.return_pressure()
+
+ full_oxy = A.oxygen * size
+ full_nitro = A.nitrogen * size
+ full_co2 = A.carbon_dioxide * size
+ full_plasma = A.toxins * size
+
+ full_heat_capacity = A.heat_capacity() * size
+
+ oxy_avg = (full_oxy + unsim_oxygen*share_size) / (size + share_size)
+ nit_avg = (full_nitro + unsim_nitrogen*share_size) / (size + share_size)
+ co2_avg = (full_co2 + unsim_co2*share_size) / (size + share_size)
+ plasma_avg = (full_plasma + unsim_plasma*share_size) / (size + share_size)
+
+ temp_avg = 0
+
+ if((full_heat_capacity + unsim_heat_capacity) > 0)
+ temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
+
+ if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[tileslen]
+
+ if(dbg_output)
+ world << "Ratio: [ratio]"
+ world << "Avg O2: [oxy_avg] N2: [nit_avg]"
+
+ A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
+ A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
+ A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
+ A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg )
+
+ A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
+
+ for(var/datum/gas/G in A.trace_gases)
+ var/G_avg = (G.moles * size) / (size + share_size)
+ G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
+
+ A.update_values()
+
+ if(dbg_output) world << "Result: [abs(old_pressure - A.return_pressure())] kPa"
+
+ return abs(old_pressure - A.return_pressure())
+
+
+proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
+ //This implements a simplistic version of the Stefan-Boltzmann law.
+ var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5
+ var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier))
+ if(maximum_energy_delta > abs(energy_delta))
+ if(energy_delta < 0)
+ maximum_energy_delta *= -1
+ energy_delta = maximum_energy_delta
+
+ A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier)
+ B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier)
\ No newline at end of file
diff --git a/code/ZAS/ConnectionManager.dm b/code/ZAS/ConnectionManager.dm
new file mode 100644
index 00000000000..1c101f4b45a
--- /dev/null
+++ b/code/ZAS/ConnectionManager.dm
@@ -0,0 +1,102 @@
+/*
+
+Overview:
+ The connection_manager class stores connections in each cardinal direction on a turf.
+ It isn't always present if a turf has no connections, check if(connections) before using.
+ Contains procs for mass manipulation of connection data.
+
+Class Vars:
+
+ NSEWUD - Connections to this turf in each cardinal direction.
+
+Class Procs:
+
+ get(d)
+ Returns the connection (if any) in this direction.
+ Preferable to accessing the connection directly because it checks validity.
+
+ place(connection/c, d)
+ Called by air_master.connect(). Sets the connection in the specified direction to c.
+
+ update_all()
+ Called after turf/update_air_properties(). Updates the validity of all connections on this turf.
+
+ erase_all()
+ Called when the turf is changed with ChangeTurf(). Erases all existing connections.
+
+ check(connection/c)
+ Checks for connection validity. It's possible to have a reference to a connection that has been erased.
+
+
+*/
+
+/turf/var/tmp/connection_manager/connections
+
+/connection_manager/var/connection/N
+/connection_manager/var/connection/S
+/connection_manager/var/connection/E
+/connection_manager/var/connection/W
+
+#ifdef ZLEVELS
+/connection_manager/var/connection/U
+/connection_manager/var/connection/D
+#endif
+
+/connection_manager/proc/get(d)
+ switch(d)
+ if(NORTH)
+ if(check(N)) return N
+ else return null
+ if(SOUTH)
+ if(check(S)) return S
+ else return null
+ if(EAST)
+ if(check(E)) return E
+ else return null
+ if(WEST)
+ if(check(W)) return W
+ else return null
+
+ #ifdef ZLEVELS
+ if(UP)
+ if(check(U)) return U
+ else return null
+ if(DOWN)
+ if(check(D)) return D
+ else return null
+ #endif
+
+/connection_manager/proc/place(connection/c, d)
+ switch(d)
+ if(NORTH) N = c
+ if(SOUTH) S = c
+ if(EAST) E = c
+ if(WEST) W = c
+
+ #ifdef ZLEVELS
+ if(UP) U = c
+ if(DOWN) D = c
+ #endif
+
+/connection_manager/proc/update_all()
+ if(check(N)) N.update()
+ if(check(S)) S.update()
+ if(check(E)) E.update()
+ if(check(W)) W.update()
+ #ifdef ZLEVELS
+ if(check(U)) U.update()
+ if(check(D)) D.update()
+ #endif
+
+/connection_manager/proc/erase_all()
+ if(check(N)) N.erase()
+ if(check(S)) S.erase()
+ if(check(E)) E.erase()
+ if(check(W)) W.erase()
+ #ifdef ZLEVELS
+ if(check(U)) U.erase()
+ if(check(D)) D.erase()
+ #endif
+
+/connection_manager/proc/check(connection/c)
+ return c && c.valid()
\ No newline at end of file
diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
new file mode 100644
index 00000000000..83b4bf3f3b4
--- /dev/null
+++ b/code/ZAS/Controller.dm
@@ -0,0 +1,321 @@
+var/datum/controller/air_system/air_master
+
+var/tick_multiplier = 2
+
+/*
+
+Overview:
+ The air controller does everything. There are tons of procs in here.
+
+Class Vars:
+ zones - All zones currently holding one or more turfs.
+ edges - All processing edges.
+
+ tiles_to_update - Tiles scheduled to update next tick.
+ zones_to_update - Zones which have had their air changed and need air archival.
+ active_hotspots - All processing fire objects.
+
+ active_zones - The number of zones which were archived last tick. Used in debug verbs.
+ next_id - The next UID to be applied to a zone. Mostly useful for debugging purposes as zones do not need UIDs to function.
+
+Class Procs:
+
+ mark_for_update(turf/T)
+ Adds the turf to the update list. When updated, update_air_properties() will be called.
+ When stuff changes that might affect airflow, call this. It's basically the only thing you need.
+
+ add_zone(zone/Z) and remove_zone(zone/Z)
+ Adds zones to the zones list. Does not mark them for update.
+
+ air_blocked(turf/A, turf/B)
+ Returns a bitflag consisting of:
+ AIR_BLOCKED - The connection between turfs is physically blocked. No air can pass.
+ ZONE_BLOCKED - There is a door between the turfs, so zones cannot cross. Air may or may not be permeable.
+
+ has_valid_zone(turf/T)
+ Checks the presence and validity of T's zone.
+ May be called on unsimulated turfs, returning 0.
+
+ merge(zone/A, zone/B)
+ Called when zones have a direct connection and equivalent pressure and temperature.
+ Merges the zones to create a single zone.
+
+ connect(turf/simulated/A, turf/B)
+ Called by turf/update_air_properties(). The first argument must be simulated.
+ Creates a connection between A and B.
+
+ mark_zone_update(zone/Z)
+ Adds zone to the update list. Unlike mark_for_update(), this one is called automatically whenever
+ air is returned from a simulated turf.
+
+ equivalent_pressure(zone/A, zone/B)
+ Currently identical to A.air.compare(B.air). Returns 1 when directly connected zones are ready to be merged.
+
+ get_edge(zone/A, zone/B)
+ get_edge(zone/A, turf/B)
+ Gets a valid connection_edge between A and B, creating a new one if necessary.
+
+ has_same_air(turf/A, turf/B)
+ Used to determine if an unsimulated edge represents a specific turf.
+ Simulated edges use connection_edge/contains_zone() for the same purpose.
+ Returns 1 if A has identical gases and temperature to B.
+
+ remove_edge(connection_edge/edge)
+ Called when an edge is erased. Removes it from processing.
+
+*/
+
+
+//Geometry lists
+/datum/controller/air_system/var/list/zones = list()
+/datum/controller/air_system/var/list/edges = list()
+
+//Geometry updates lists
+/datum/controller/air_system/var/list/tiles_to_update = list()
+/datum/controller/air_system/var/list/zones_to_update = list()
+/datum/controller/air_system/var/list/active_hotspots = list()
+
+/datum/controller/air_system/var/active_zones = 0
+
+/datum/controller/air_system/var/current_cycle = 0
+/datum/controller/air_system/var/update_delay = 5 //How long between check should it try to process atmos again.
+/datum/controller/air_system/var/failed_ticks = 0 //How many ticks have runtimed?
+
+/datum/controller/air_system/var/tick_progress = 0
+
+/datum/controller/air_system/var/next_id = 1 //Used to keep track of zone UIDs.
+
+/datum/controller/air_system/proc/Setup()
+ //Purpose: Call this at the start to setup air groups geometry
+ // (Warning: Very processor intensive but only must be done once per round)
+ //Called by: Gameticker/Master controller
+ //Inputs: None.
+ //Outputs: None.
+
+ #ifndef ZASDBG
+ set background = 1
+ #endif
+
+ world << "\red \b Processing Geometry..."
+ sleep(-1)
+
+ var/start_time = world.timeofday
+
+ var/simulated_turf_count = 0
+
+ for(var/turf/simulated/S in world)
+ simulated_turf_count++
+ S.update_air_properties()
+
+ world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.
+Total Simulated Turfs: [simulated_turf_count]
+Total Zones: [zones.len]
+Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"}
+
+// spawn Start()
+
+
+/datum/controller/air_system/proc/Start()
+ //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere.
+ //Called by: Master controller
+ //Inputs: None.
+ //Outputs: None.
+
+ #ifndef ZASDBG
+ set background = 1
+ #endif
+
+ while(1)
+ Tick()
+ sleep(max(5,update_delay*tick_multiplier))
+
+
+/datum/controller/air_system/proc/Tick()
+ . = 1 //Set the default return value, for runtime detection.
+
+ current_cycle++
+
+ //If there are tiles to update, do so.
+ tick_progress = "updating turf properties"
+
+ var/list/updating
+
+ if(tiles_to_update.len)
+ updating = tiles_to_update
+ tiles_to_update = list()
+
+ #ifdef ZASDBG
+ var/updated = 0
+ #endif
+ for(var/turf/T in updating)
+ T.update_air_properties()
+ T.post_update_air_properties()
+ T.needs_air_update = 0
+ #ifdef ZASDBG
+ T.overlays -= mark
+ updated++
+ #endif
+ //sleep(1)
+
+ #ifdef ZASDBG
+ if(updated != updating.len)
+ tick_progress = "[updating.len - updated] tiles left unupdated."
+ world << "\red [tick_progress]"
+ . = 0
+ #endif
+
+ //Where gas exchange happens.
+ if(.)
+ tick_progress = "processing edges"
+
+ for(var/connection_edge/edge in edges)
+ edge.tick()
+
+ //Process fires.
+ if(.)
+ tick_progress = "processing fire"
+
+ for(var/obj/fire/fire in active_hotspots)
+ fire.process()
+
+ //Process zones.
+ if(.)
+ tick_progress = "updating zones"
+
+ active_zones = zones_to_update.len
+ if(zones_to_update.len)
+ updating = zones_to_update
+ zones_to_update = list()
+ for(var/zone/zone in updating)
+ zone.tick()
+ zone.needs_update = 0
+
+ if(.)
+ tick_progress = "success"
+
+/datum/controller/air_system/proc/add_zone(zone/z)
+ zones.Add(z)
+ z.name = "Zone [next_id++]"
+ mark_zone_update(z)
+
+/datum/controller/air_system/proc/remove_zone(zone/z)
+ zones.Remove(z)
+
+/datum/controller/air_system/proc/air_blocked(turf/A, turf/B)
+ #ifdef ZASDBG
+ ASSERT(isturf(A))
+ ASSERT(isturf(B))
+ #endif
+ var/ablock = A.c_airblock(B)
+ if(ablock == BLOCKED) return BLOCKED
+ return ablock | B.c_airblock(A)
+
+/datum/controller/air_system/proc/has_valid_zone(turf/simulated/T)
+ #ifdef ZASDBG
+ ASSERT(istype(T))
+ #endif
+ return istype(T) && T.zone && !T.zone.invalid
+
+/datum/controller/air_system/proc/merge(zone/A, zone/B)
+ #ifdef ZASDBG
+ ASSERT(istype(A))
+ ASSERT(istype(B))
+ ASSERT(!A.invalid)
+ ASSERT(!B.invalid)
+ ASSERT(A != B)
+ #endif
+ if(A.contents.len < B.contents.len)
+ A.c_merge(B)
+ mark_zone_update(B)
+ else
+ B.c_merge(A)
+ mark_zone_update(A)
+
+/datum/controller/air_system/proc/connect(turf/simulated/A, turf/simulated/B)
+ #ifdef ZASDBG
+ ASSERT(istype(A))
+ ASSERT(isturf(B))
+ ASSERT(A.zone)
+ ASSERT(!A.zone.invalid)
+ //ASSERT(B.zone)
+ ASSERT(A != B)
+ #endif
+
+ var/block = air_master.air_blocked(A,B)
+ if(block & AIR_BLOCKED) return
+
+ var/direct = !(block & ZONE_BLOCKED)
+ var/space = !istype(B)
+
+ if(direct && !space)
+ if(equivalent_pressure(A.zone,B.zone) || current_cycle == 0)
+ merge(A.zone,B.zone)
+ return
+
+ var
+ a_to_b = get_dir(A,B)
+ b_to_a = get_dir(B,A)
+
+ if(!A.connections) A.connections = new
+ if(!B.connections) B.connections = new
+
+ if(A.connections.get(a_to_b)) return
+ if(B.connections.get(b_to_a)) return
+ if(!space)
+ if(A.zone == B.zone) return
+
+
+ var/connection/c = new /connection(A,B)
+
+ A.connections.place(c, a_to_b)
+ B.connections.place(c, b_to_a)
+
+ if(direct) c.mark_direct()
+
+/datum/controller/air_system/proc/mark_for_update(turf/T)
+ #ifdef ZASDBG
+ ASSERT(isturf(T))
+ #endif
+ if(T.needs_air_update) return
+ tiles_to_update |= T
+ #ifdef ZASDBG
+ T.overlays += mark
+ #endif
+ T.needs_air_update = 1
+
+/datum/controller/air_system/proc/mark_zone_update(zone/Z)
+ #ifdef ZASDBG
+ ASSERT(istype(Z))
+ #endif
+ if(Z.needs_update) return
+ zones_to_update.Add(Z)
+ Z.needs_update = 1
+
+/datum/controller/air_system/proc/equivalent_pressure(zone/A, zone/B)
+ return A.air.compare(B.air)
+
+/datum/controller/air_system/proc/get_edge(zone/A, zone/B)
+
+ if(istype(B))
+ for(var/connection_edge/zone/edge in A.edges)
+ if(edge.contains_zone(B)) return edge
+ var/connection_edge/edge = new/connection_edge/zone(A,B)
+ edges.Add(edge)
+ return edge
+ else
+ for(var/connection_edge/unsimulated/edge in A.edges)
+ if(has_same_air(edge.B,B)) return edge
+ var/connection_edge/edge = new/connection_edge/unsimulated(A,B)
+ edges.Add(edge)
+ return edge
+
+/datum/controller/air_system/proc/has_same_air(turf/A, turf/B)
+ if(A.oxygen != B.oxygen) return 0
+ if(A.nitrogen != B.nitrogen) return 0
+ if(A.toxins != B.toxins) return 0
+ if(A.carbon_dioxide != B.carbon_dioxide) return 0
+ if(A.temperature != B.temperature) return 0
+ return 1
+
+/datum/controller/air_system/proc/remove_edge(connection/c)
+ edges.Remove(c)
\ No newline at end of file
diff --git a/code/ZAS/Debug.dm b/code/ZAS/Debug.dm
index 39f5a7caabd..6f0a67fd3eb 100644
--- a/code/ZAS/Debug.dm
+++ b/code/ZAS/Debug.dm
@@ -1,219 +1,20 @@
-client/proc/ZoneTick()
- set category = "Debug"
- set name = "Process Atmos"
+var/image/assigned = image('icons/Testing/Zone.dmi', icon_state = "assigned")
+var/image/created = image('icons/Testing/Zone.dmi', icon_state = "created")
+var/image/merged = image('icons/Testing/Zone.dmi', icon_state = "merged")
+var/image/invalid_zone = image('icons/Testing/Zone.dmi', icon_state = "invalid")
+var/image/air_blocked = image('icons/Testing/Zone.dmi', icon_state = "block")
+var/image/zone_blocked = image('icons/Testing/Zone.dmi', icon_state = "zoneblock")
+var/image/blocked = image('icons/Testing/Zone.dmi', icon_state = "fullblock")
+var/image/mark = image('icons/Testing/Zone.dmi', icon_state = "mark")
- var/result = air_master.Tick()
- if(result)
- src << "Sucessfully Processed."
+/connection_edge/var/dbg_out = 0
- else
- src << "Failed to process! ([air_master.tick_progress])"
+/turf/var/tmp/dbg_img
+/turf/proc/dbg(image/img, d = 0)
+ if(d > 0) img.dir = d
+ overlays -= dbg_img
+ overlays += img
+ dbg_img = img
-
-client/proc/Zone_Info(turf/T as null|turf)
- set category = "Debug"
- if(T)
- if(T.zone)
- T.zone.DebugDisplay(src)
- else
- mob << "No zone here."
- else
- if(zone_debug_images)
- for(var/zone in zone_debug_images)
- images -= zone_debug_images[zone]
- zone_debug_images = null
-
-client/var/list/zone_debug_images
-
-client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
- set category = "Debug"
- if(!istype(T))
- return
-
- var/direction_list = list(\
- "North" = NORTH,\
- "South" = SOUTH,\
- "East" = EAST,\
- "West" = WEST,\
- "N/A" = null)
- var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
- if(!direction)
- return
-
- if(direction == "N/A")
- if(T.CanPass(null, T, 0,0))
- mob << "The turf can pass air! :D"
- else
- mob << "No air passage :x"
- return
-
- var/turf/simulated/other_turf = get_step(T, direction_list[direction])
- if(!istype(other_turf))
- return
-
- var/pass_directions = T.CanPass(null, other_turf, 0, 0) + 2 * other_turf.CanPass(null, T, 0, 0)
-
- switch(pass_directions)
- if(0)
- mob << "Neither turf can connect. :("
-
- if(1)
- mob << "The initial turf only can connect. :\\"
-
- if(2)
- mob << "The other turf can connect, but not the initial turf. :/"
-
- if(3)
- mob << "Both turfs can connect! :)"
-
-
-zone/proc/DebugDisplay(client/client)
- if(!istype(client))
- return
-
- if(!dbg_output)
- dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
-
- if(!client.zone_debug_images)
- client.zone_debug_images = list()
-
- var/list/current_zone_images = list()
-
- for(var/turf/T in contents)
- current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
-
- for(var/turf/space/S in unsimulated_tiles)
- current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
-
- client << "Zone Air Contents"
- client << "Oxygen: [air.oxygen]"
- client << "Nitrogen: [air.nitrogen]"
- client << "Plasma: [air.toxins]"
- client << "Carbon Dioxide: [air.carbon_dioxide]"
- client << "Temperature: [air.temperature] K"
- client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
- client << "Pressure: [air.return_pressure()] KPa"
- client << ""
- client << "Space Tiles: [length(unsimulated_tiles)]"
- client << "Movable Objects: [length(movables())]"
- client << "Connections: [length(connections)]"
-
- for(var/connection/C in connections)
- client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
- current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
- current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
-
- client << "Connected Zones:"
- for(var/zone/zone in connected_zones)
- client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
-
- for(var/zone/zone in closed_connection_zones)
- client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
-
- for(var/C in connections)
- if(!istype(C,/connection))
- client << "[C] (Not Connection!)"
-
- if(!client.zone_debug_images)
- client.zone_debug_images = list()
- client.zone_debug_images[src] = current_zone_images
-
- client.images += client.zone_debug_images[src]
-
- else
- dbg_output = 0
-
- client.images -= client.zone_debug_images[src]
- client.zone_debug_images.Remove(src)
-
- if(air_master)
- for(var/zone/Z in air_master.zones)
- if(Z.air == air && Z != src)
- var/turf/zloc = pick(Z.contents)
- client << "\red Illegal air datum shared by: [zloc.loc.name]"
-
-
-client/proc/TestZASRebuild()
- set category = "Debug"
-// var/turf/turf = get_turf(mob)
- var/zone/current_zone = mob.loc:zone
- if(!current_zone)
- src << "There is no zone there!"
- return
-
- var/list/current_adjacents = list()
- var/list/overlays = list()
- var/adjacent_id
- var/lowest_id
-
- var/list/identical_ids = list()
- var/list/turfs = current_zone.contents.Copy()
- var/current_identifier = 1
-
- for(var/turf/simulated/current in turfs)
- lowest_id = null
- current_adjacents = list()
-
- for(var/direction in cardinal)
- var/turf/simulated/adjacent = get_step(current, direction)
- if(!current.ZCanPass(adjacent))
- continue
- if(turfs.Find(adjacent))
- current_adjacents += adjacent
- adjacent_id = turfs[adjacent]
-
- if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
- lowest_id = adjacent_id
-
- if(!lowest_id)
- lowest_id = current_identifier++
- identical_ids += lowest_id
- overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
-
- for(var/turf/simulated/adjacent in current_adjacents)
- adjacent_id = turfs[adjacent]
- if(adjacent_id != lowest_id)
- if(adjacent_id)
- adjacent.overlays -= overlays[adjacent_id]
- identical_ids[adjacent_id] = lowest_id
-
- turfs[adjacent] = lowest_id
- adjacent.overlays += overlays[lowest_id]
-
- sleep(5)
-
- if(turfs[current])
- current.overlays -= overlays[turfs[current]]
- turfs[current] = lowest_id
- current.overlays += overlays[lowest_id]
- sleep(5)
-
- var/list/final_arrangement = list()
-
- for(var/turf/simulated/current in turfs)
- current_identifier = identical_ids[turfs[current]]
- current.overlays -= overlays[turfs[current]]
- current.overlays += overlays[current_identifier]
- sleep(5)
-
- if( current_identifier > final_arrangement.len )
- final_arrangement.len = current_identifier
- final_arrangement[current_identifier] = list(current)
-
- else
- final_arrangement[current_identifier] += current
-
- //lazy but fast
- final_arrangement.Remove(null)
-
- src << "There are [final_arrangement.len] unique segments."
-
- for(var/turf/current in turfs)
- current.overlays -= overlays
-
- return final_arrangement
-
-/client/proc/ZASSettings()
- set category = "Debug"
-
- vsc.SetDefault(mob)
\ No newline at end of file
+proc/soft_assert(thing,fail)
+ if(!thing) message_admins(fail)
\ No newline at end of file
diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm
new file mode 100644
index 00000000000..eb5d72788e2
--- /dev/null
+++ b/code/ZAS/Diagnostic.dm
@@ -0,0 +1,236 @@
+client/proc/ZoneTick()
+ set category = "Debug"
+ set name = "Process Atmos"
+
+ var/result = air_master.Tick()
+ if(result)
+ src << "Sucessfully Processed."
+
+ else
+ src << "Failed to process! ([air_master.tick_progress])"
+
+
+client/proc/Zone_Info(turf/T as null|turf)
+ set category = "Debug"
+ if(T)
+ if(istype(T,/turf/simulated) && T:zone)
+ T:zone:dbg_data(src)
+ else
+ mob << "No zone here."
+ var/datum/gas_mixture/mix = T.return_air()
+ mob << "[mix.return_pressure()] kPa [mix.temperature]C"
+ mob << "O2: [mix.oxygen] N2: [mix.nitrogen] CO2: [mix.carbon_dioxide] TX: [mix.toxins]"
+ else
+ if(zone_debug_images)
+ for(var/zone in zone_debug_images)
+ images -= zone_debug_images[zone]
+ zone_debug_images = null
+
+client/var/list/zone_debug_images
+
+client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
+ set category = "Debug"
+ if(!istype(T))
+ return
+
+ var/direction_list = list(\
+ "North" = NORTH,\
+ "South" = SOUTH,\
+ "East" = EAST,\
+ "West" = WEST,\
+ "N/A" = null)
+ var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
+ if(!direction)
+ return
+
+ if(direction == "N/A")
+ if(!(T.c_airblock(T) & AIR_BLOCKED))
+ mob << "The turf can pass air! :D"
+ else
+ mob << "No air passage :x"
+ return
+
+ var/turf/simulated/other_turf = get_step(T, direction_list[direction])
+ if(!istype(other_turf))
+ return
+
+ var/t_block = T.c_airblock(other_turf)
+ var/o_block = other_turf.c_airblock(T)
+
+ if(o_block & AIR_BLOCKED)
+ if(t_block & AIR_BLOCKED)
+ mob << "Neither turf can connect. :("
+
+ else
+ mob << "The initial turf only can connect. :\\"
+ else
+ if(t_block & AIR_BLOCKED)
+ mob << "The other turf can connect, but not the initial turf. :/"
+
+ else
+ mob << "Both turfs can connect! :)"
+
+ mob << "Additionally, \..."
+
+ if(o_block & ZONE_BLOCKED)
+ if(t_block & ZONE_BLOCKED)
+ mob << "neither turf can merge."
+ else
+ mob << "the other turf cannot merge."
+ else
+ if(t_block & ZONE_BLOCKED)
+ mob << "the initial turf cannot merge."
+ else
+ mob << "both turfs can merge."
+
+
+/*zone/proc/DebugDisplay(client/client)
+ if(!istype(client))
+ return
+
+ if(!dbg_output)
+ dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
+
+ if(!client.zone_debug_images)
+ client.zone_debug_images = list()
+
+ var/list/current_zone_images = list()
+
+ for(var/turf/T in contents)
+ current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
+
+ for(var/turf/space/S in unsimulated_tiles)
+ current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
+
+ client << "Zone Air Contents"
+ client << "Oxygen: [air.oxygen]"
+ client << "Nitrogen: [air.nitrogen]"
+ client << "Plasma: [air.toxins]"
+ client << "Carbon Dioxide: [air.carbon_dioxide]"
+ client << "Temperature: [air.temperature] K"
+ client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
+ client << "Pressure: [air.return_pressure()] KPa"
+ client << ""
+ client << "Space Tiles: [length(unsimulated_tiles)]"
+ client << "Movable Objects: [length(movables())]"
+ client << "Connections: [length(connections)]"
+
+ for(var/connection/C in connections)
+ client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
+ current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
+ current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
+
+ client << "Connected Zones:"
+ for(var/zone/zone in connected_zones)
+ client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
+
+ for(var/zone/zone in closed_connection_zones)
+ client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
+
+ for(var/C in connections)
+ if(!istype(C,/connection))
+ client << "[C] (Not Connection!)"
+
+ if(!client.zone_debug_images)
+ client.zone_debug_images = list()
+ client.zone_debug_images[src] = current_zone_images
+
+ client.images += client.zone_debug_images[src]
+
+ else
+ dbg_output = 0
+
+ client.images -= client.zone_debug_images[src]
+ client.zone_debug_images.Remove(src)
+
+ if(air_master)
+ for(var/zone/Z in air_master.zones)
+ if(Z.air == air && Z != src)
+ var/turf/zloc = pick(Z.contents)
+ client << "\red Illegal air datum shared by: [zloc.loc.name]"*/
+
+
+/*client/proc/TestZASRebuild()
+ set category = "Debug"
+// var/turf/turf = get_turf(mob)
+ var/zone/current_zone = mob.loc:zone
+ if(!current_zone)
+ src << "There is no zone there!"
+ return
+
+ var/list/current_adjacents = list()
+ var/list/overlays = list()
+ var/adjacent_id
+ var/lowest_id
+
+ var/list/identical_ids = list()
+ var/list/turfs = current_zone.contents.Copy()
+ var/current_identifier = 1
+
+ for(var/turf/simulated/current in turfs)
+ lowest_id = null
+ current_adjacents = list()
+
+ for(var/direction in cardinal)
+ var/turf/simulated/adjacent = get_step(current, direction)
+ if(!current.ZCanPass(adjacent))
+ continue
+ if(turfs.Find(adjacent))
+ current_adjacents += adjacent
+ adjacent_id = turfs[adjacent]
+
+ if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
+ lowest_id = adjacent_id
+
+ if(!lowest_id)
+ lowest_id = current_identifier++
+ identical_ids += lowest_id
+ overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
+
+ for(var/turf/simulated/adjacent in current_adjacents)
+ adjacent_id = turfs[adjacent]
+ if(adjacent_id != lowest_id)
+ if(adjacent_id)
+ adjacent.overlays -= overlays[adjacent_id]
+ identical_ids[adjacent_id] = lowest_id
+
+ turfs[adjacent] = lowest_id
+ adjacent.overlays += overlays[lowest_id]
+
+ sleep(5)
+
+ if(turfs[current])
+ current.overlays -= overlays[turfs[current]]
+ turfs[current] = lowest_id
+ current.overlays += overlays[lowest_id]
+ sleep(5)
+
+ var/list/final_arrangement = list()
+
+ for(var/turf/simulated/current in turfs)
+ current_identifier = identical_ids[turfs[current]]
+ current.overlays -= overlays[turfs[current]]
+ current.overlays += overlays[current_identifier]
+ sleep(5)
+
+ if( current_identifier > final_arrangement.len )
+ final_arrangement.len = current_identifier
+ final_arrangement[current_identifier] = list(current)
+
+ else
+ final_arrangement[current_identifier] += current
+
+ //lazy but fast
+ final_arrangement.Remove(null)
+
+ src << "There are [final_arrangement.len] unique segments."
+
+ for(var/turf/current in turfs)
+ current.overlays -= overlays
+
+ return final_arrangement*/
+
+client/proc/ZASSettings()
+ set category = "Debug"
+
+ vsc.SetDefault(mob)
\ No newline at end of file
diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm
index 73033f9bdc7..a564de2836f 100644
--- a/code/ZAS/Fire.dm
+++ b/code/ZAS/Fire.dm
@@ -28,12 +28,6 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
if(!air_contents || exposed_temperature < PLASMA_MINIMUM_BURN_TEMPERATURE)
return 0
- var/obj/structure/reagent_dispensers/fueltank/FT = locate() in src
- if(exposed_temperature >= AUTOIGNITION_WELDERFUEL)
- if (FT)
- FT.explode()
- return 1
-
var/igniting = 0
var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in src
@@ -81,12 +75,12 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
//since the air is processed in fractions, we need to make sure not to have any minuscle residue or
//the amount of moles might get to low for some functions to catch them and thus result in wonky behaviour
- if(air_contents.oxygen < 0.001)
+ if(air_contents.oxygen < 0.1)
air_contents.oxygen = 0
- if(air_contents.toxins < 0.001)
+ if(air_contents.toxins < 0.1)
air_contents.toxins = 0
if(fuel)
- if(fuel.moles < 0.001)
+ if(fuel.moles < 0.1)
air_contents.trace_gases.Remove(fuel)
//check if there is something to combust
@@ -114,7 +108,7 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
//spread
for(var/direction in cardinal)
- if(S.air_check_directions&direction) //Grab all valid bordering tiles
+ if(S.open_directions & direction) //Grab all valid bordering tiles
var/turf/simulated/enemy_tile = get_step(S, direction)
@@ -206,7 +200,7 @@ datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, forc
if(liquid)
//Liquid Fuel
- if(liquid.amount <= 0)
+ if(liquid.amount <= 0.1)
del liquid
else
total_fuel += liquid.amount
@@ -263,9 +257,9 @@ datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_
if(oxygen && (toxins || fuel || liquid))
if(liquid)
return 1
- if (toxins)
+ if(toxins >= 0.1)
return 1
- if(fuel)
+ if(fuel && fuel.moles >= 0.1)
return 1
return 0
@@ -278,9 +272,9 @@ datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fu
if(oxygen && (toxins || fuel || liquid))
if(liquid)
return 1
- if (toxins >= 0.7)
+ if (toxins >= 0.1)
return 1
- if(fuel && fuel.moles >= 1.4)
+ if(fuel && fuel.moles >= 0.1)
return 1
return 0
diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm
new file mode 100644
index 00000000000..fece782aa71
--- /dev/null
+++ b/code/ZAS/Turf.dm
@@ -0,0 +1,241 @@
+/turf/simulated/var/zone/zone
+/turf/simulated/var/open_directions
+/turf/simulated/var/gas_graphic
+
+/turf/var/needs_air_update = 0
+/turf/var/datum/gas_mixture/air
+
+/turf/simulated/proc/set_graphic(new_graphic)
+ if(isnum(new_graphic))
+ if(new_graphic == 1) new_graphic = plmaster
+ else if(new_graphic == 2) new_graphic = slmaster
+ if(gas_graphic) overlays -= gas_graphic
+ if(new_graphic) overlays += new_graphic
+ gas_graphic = new_graphic
+
+/turf/proc/update_air_properties()
+ var/block = c_airblock(src)
+ if(block & AIR_BLOCKED)
+ //dbg(blocked)
+ return 1
+
+ #ifdef ZLEVELS
+ for(var/d = 1, d < 64, d *= 2)
+ #else
+ for(var/d = 1, d < 16, d *= 2)
+ #endif
+
+ var/turf/unsim = get_step(src, d)
+ block = unsim.c_airblock(src)
+
+ if(block & AIR_BLOCKED)
+ //unsim.dbg(air_blocked, turn(180,d))
+ continue
+
+ var/r_block = c_airblock(unsim)
+
+ if(r_block & AIR_BLOCKED)
+ continue
+
+ if(istype(unsim, /turf/simulated))
+
+ var/turf/simulated/sim = unsim
+ if(air_master.has_valid_zone(sim))
+
+ air_master.connect(sim, src)
+
+/turf/simulated/update_air_properties()
+ if(zone && zone.invalid)
+ c_copy_air()
+ zone = null //Easier than iterating through the list at the zone.
+
+ var/s_block = c_airblock(src)
+ if(s_block & AIR_BLOCKED)
+ #ifdef ZASDBG
+ if(verbose) world << "Self-blocked."
+ //dbg(blocked)
+ #endif
+ if(zone)
+ var/zone/z = zone
+ if(locate(/obj/machinery/door/airlock) in src) //Hacky, but prevents normal airlocks from rebuilding zones all the time
+ z.remove(src)
+ else
+ z.rebuild()
+
+ return 1
+
+ var/previously_open = open_directions
+ open_directions = 0
+
+ var/list/postponed
+ #ifdef ZLEVELS
+ for(var/d = 1, d < 64, d *= 2)
+ #else
+ for(var/d = 1, d < 16, d *= 2)
+ #endif
+
+ var/turf/unsim = get_step(src, d)
+ var/block = unsim.c_airblock(src)
+ if(block & AIR_BLOCKED)
+
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is blocked."
+ //unsim.dbg(air_blocked, turn(180,d))
+ #endif
+
+ continue
+
+ var/r_block = c_airblock(unsim)
+ if(r_block & AIR_BLOCKED)
+
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is blocked."
+ //dbg(air_blocked, d)
+ #endif
+
+ //Check that our zone hasn't been cut off recently.
+ //This happens when windows move or are constructed. We need to rebuild.
+ if((previously_open & d) && istype(unsim, /turf/simulated))
+ var/turf/simulated/sim = unsim
+ if(sim.zone == zone)
+ zone.rebuild()
+ return
+
+ continue
+
+ open_directions |= d
+
+ if(istype(unsim, /turf/simulated))
+
+ var/turf/simulated/sim = unsim
+ if(air_master.has_valid_zone(sim))
+
+ //Might have assigned a zone, since this happens for each direction.
+ if(!zone)
+
+ //if((block & ZONE_BLOCKED) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
+ if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is zone blocked."
+ //dbg(zone_blocked, d)
+ #endif
+
+ //Postpone this tile rather than exit, since a connection can still be made.
+ if(!postponed) postponed = list()
+ postponed.Add(sim)
+
+ else
+
+ sim.zone.add(src)
+
+ #ifdef ZASDBG
+ dbg(assigned)
+ if(verbose) world << "Added to [zone]"
+ #endif
+
+ else if(sim.zone != zone)
+
+ #ifdef ZASDBG
+ if(verbose) world << "Connecting to [sim.zone]"
+ #endif
+
+ air_master.connect(src, sim)
+
+
+ #ifdef ZASDBG
+ else if(verbose) world << "[d] has same zone."
+
+ else if(verbose) world << "[d] has invalid zone."
+ #endif
+
+ else
+
+ //Postponing connections to tiles until a zone is assured.
+ if(!postponed) postponed = list()
+ postponed.Add(unsim)
+
+ if(!air_master.has_valid_zone(src)) //Still no zone, make a new one.
+ var/zone/newzone = new/zone()
+ newzone.add(src)
+
+ #ifdef ZASDBG
+ dbg(created)
+
+ ASSERT(zone)
+ #endif
+
+ //At this point, a zone should have happened. If it hasn't, don't add more checks, fix the bug.
+
+ for(var/turf/T in postponed)
+ air_master.connect(src, T)
+
+/turf/proc/post_update_air_properties()
+ if(connections) connections.update_all()
+
+/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
+ del(giver)
+ return 0
+
+/turf/return_air()
+ //Create gas mixture to hold data for passing
+ var/datum/gas_mixture/GM = new
+
+ GM.oxygen = oxygen
+ GM.carbon_dioxide = carbon_dioxide
+ GM.nitrogen = nitrogen
+ GM.toxins = toxins
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+/turf/remove_air(amount as num)
+ var/datum/gas_mixture/GM = new
+
+ var/sum = oxygen + carbon_dioxide + nitrogen + toxins
+ if(sum>0)
+ GM.oxygen = (oxygen/sum)*amount
+ GM.carbon_dioxide = (carbon_dioxide/sum)*amount
+ GM.nitrogen = (nitrogen/sum)*amount
+ GM.toxins = (toxins/sum)*amount
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+/turf/simulated/assume_air(datum/gas_mixture/giver)
+ var/datum/gas_mixture/my_air = return_air()
+ my_air.merge(giver)
+
+/turf/simulated/remove_air(amount as num)
+ var/datum/gas_mixture/my_air = return_air()
+ return my_air.remove(amount)
+
+/turf/simulated/return_air()
+ if(zone)
+ if(!zone.invalid)
+ air_master.mark_zone_update(zone)
+ return zone.air
+ else
+ if(!air)
+ make_air()
+ c_copy_air()
+ return air
+ else
+ if(!air)
+ make_air()
+ return air
+
+/turf/proc/make_air()
+ air = new/datum/gas_mixture
+ air.temperature = temperature
+ air.adjust(oxygen, carbon_dioxide, nitrogen, toxins)
+ air.group_multiplier = 1
+ air.volume = CELL_VOLUME
+
+/turf/simulated/proc/c_copy_air()
+ if(!air) air = new/datum/gas_mixture
+ air.copy_from(zone.air)
+ air.group_multiplier = 1
\ No newline at end of file
diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm
new file mode 100644
index 00000000000..349d0e0582f
--- /dev/null
+++ b/code/ZAS/Zone.dm
@@ -0,0 +1,154 @@
+/*
+
+Overview:
+ Each zone is a self-contained area where gas values would be the same if tile-based equalization were run indefinitely.
+ If you're unfamiliar with ZAS, FEA's air groups would have similar functionality if they didn't break in a stiff breeze.
+
+Class Vars:
+ name - A name of the format "Zone [#]", used for debugging.
+ invalid - True if the zone has been erased and is no longer eligible for processing.
+ needs_update - True if the zone has been added to the update list.
+ edges - A list of edges that connect to this zone.
+ air - The gas mixture that any turfs in this zone will return. Values are per-tile with a group multiplier.
+
+Class Procs:
+ add(turf/simulated/T)
+ Adds a turf to the contents, sets its zone and merges its air.
+
+ remove(turf/simulated/T)
+ Removes a turf, sets its zone to null and erases any gas graphics.
+ Invalidates the zone if it has no more tiles.
+
+ c_merge(zone/into)
+ Invalidates this zone and adds all its former contents to into.
+
+ c_invalidate()
+ Marks this zone as invalid and removes it from processing.
+
+ rebuild()
+ Invalidates the zone and marks all its former tiles for updates.
+
+ add_tile_air(turf/simulated/T)
+ Adds the air contained in T.air to the zone's air supply. Called when adding a turf.
+
+ tick()
+ Called only when the gas content is changed. Archives values and changes gas graphics.
+
+ dbg_data(mob/M)
+ Sends M a printout of important figures for the zone.
+
+*/
+
+
+/zone/var/name
+/zone/var/invalid = 0
+/zone/var/list/contents = list()
+
+/zone/var/needs_update = 0
+
+/zone/var/list/edges = list()
+
+/zone/var/datum/gas_mixture/air = new
+
+/zone/New()
+ air_master.add_zone(src)
+ air.temperature = TCMB
+ air.group_multiplier = 1
+ air.volume = CELL_VOLUME
+
+/zone/proc/add(turf/simulated/T)
+#ifdef ZASDBG
+ ASSERT(!invalid)
+ ASSERT(istype(T))
+ ASSERT(!air_master.has_valid_zone(T))
+#endif
+
+ var/datum/gas_mixture/turf_air = T.return_air()
+ add_tile_air(turf_air)
+ T.zone = src
+ contents.Add(T)
+ T.set_graphic(air.graphic)
+
+/zone/proc/remove(turf/simulated/T)
+#ifdef ZASDBG
+ ASSERT(!invalid)
+ ASSERT(istype(T))
+ ASSERT(T.zone == src)
+ soft_assert(T in contents, "Lists are weird broseph")
+#endif
+ contents.Remove(T)
+ T.zone = null
+ T.set_graphic(0)
+ if(contents.len)
+ air.group_multiplier = contents.len
+ else
+ c_invalidate()
+
+/zone/proc/c_merge(zone/into)
+#ifdef ZASDBG
+ ASSERT(!invalid)
+ ASSERT(istype(into))
+ ASSERT(into != src)
+ ASSERT(!into.invalid)
+#endif
+ c_invalidate()
+ for(var/turf/simulated/T in contents)
+ into.add(T)
+ #ifdef ZASDBG
+ T.dbg(merged)
+ #endif
+
+/zone/proc/c_invalidate()
+ invalid = 1
+ air_master.remove_zone(src)
+ #ifdef ZASDBG
+ for(var/turf/simulated/T in contents)
+ T.dbg(invalid_zone)
+ #endif
+
+/zone/proc/rebuild()
+ if(invalid) return //Short circuit for explosions where rebuild is called many times over.
+ c_invalidate()
+ for(var/turf/simulated/T in contents)
+ //T.dbg(invalid_zone)
+ T.needs_air_update = 0 //Reset the marker so that it will be added to the list.
+ air_master.mark_for_update(T)
+
+/zone/proc/add_tile_air(datum/gas_mixture/tile_air)
+ //air.volume += CELL_VOLUME
+ air.group_multiplier = 1
+ air.multiply(contents.len)
+ air.merge(tile_air)
+ air.divide(contents.len+1)
+ air.group_multiplier = contents.len+1
+
+/zone/proc/tick()
+ air.archive()
+ if(air.check_tile_graphic())
+ for(var/turf/simulated/T in contents)
+ T.set_graphic(air.graphic)
+
+/zone/proc/dbg_data(mob/M)
+ M << name
+ M << "O2: [air.oxygen] N2: [air.nitrogen] CO2: [air.carbon_dioxide] P: [air.toxins]"
+ M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)"
+ M << "O2 per N2: [(air.nitrogen ? air.oxygen/air.nitrogen : "N/A")] Moles: [air.total_moles]"
+ M << "Simulated: [contents.len] ([air.group_multiplier])"
+ //M << "Unsimulated: [unsimulated_contents.len]"
+ //M << "Edges: [edges.len]"
+ if(invalid) M << "Invalid!"
+ var/zone_edges = 0
+ var/space_edges = 0
+ var/space_coefficient = 0
+ for(var/connection_edge/E in edges)
+ if(E.type == /connection_edge/zone) zone_edges++
+ else
+ space_edges++
+ space_coefficient += E.coefficient
+ M << "[E:air:return_pressure()]kPa"
+
+ M << "Zone Edges: [zone_edges]"
+ M << "Space Edges: [space_edges] ([space_coefficient] connections)"
+
+ //for(var/turf/T in unsimulated_contents)
+ // M << "[T] at ([T.x],[T.y])"
\ No newline at end of file
diff --git a/code/ZAS/_docs.dm b/code/ZAS/_docs.dm
new file mode 100644
index 00000000000..8d084dc4c95
--- /dev/null
+++ b/code/ZAS/_docs.dm
@@ -0,0 +1,35 @@
+/*
+
+Zone Air System:
+
+This air system divides the station into impermeable areas called zones.
+When something happens, i.e. a door opening or a wall being taken down,
+zones equalize and eventually merge. Making an airtight area closes the connection again.
+
+Control Flow:
+Every air tick:
+ Marked turfs are updated with update_air_properties(), followed by post_update_air_properties().
+ Edges, including those generated by connections in the previous step, are processed. This is where gas is exchanged.
+ Fire is processed.
+ Marked zones have their air archived.
+
+Important Functions:
+
+air_master.mark_for_update(turf)
+ When stuff happens, call this. It works on everything. You basically don't need to worry about any other
+ functions besides CanPass().
+
+Notes for people who used ZAS before:
+ There is no connected_zones anymore.
+ To get the zones that are connected to a zone, use this loop:
+ for(var/connection_edge/zone/edge in zone.edges)
+ var/zone/connected_zone = edge.get_connected_zone(zone)
+
+*/
+
+//#define ZASDBG
+//#define ZLEVELS
+
+#define AIR_BLOCKED 1
+#define ZONE_BLOCKED 2
+#define BLOCKED 3
\ No newline at end of file
diff --git a/code/ZAS/_gas_mixture.dm b/code/ZAS/_gas_mixture.dm
new file mode 100644
index 00000000000..9a829633e2b
--- /dev/null
+++ b/code/ZAS/_gas_mixture.dm
@@ -0,0 +1,1090 @@
+/*
+What are the archived variables for?
+ Calculations are done using the archived variables with the results merged into the regular variables.
+ This prevents race conditions that arise based on the order of tile processing.
+*/
+
+#define SPECIFIC_HEAT_TOXIN 200
+#define SPECIFIC_HEAT_AIR 20
+#define SPECIFIC_HEAT_CDO 30
+#define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) \
+ max(0, carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN)
+
+#define MINIMUM_HEAT_CAPACITY 0.0003
+#define QUANTIZE(variable) (round(variable,0.0001))
+#define TRANSFER_FRACTION 5 //What fraction (1/#) of the air difference to try and transfer
+
+/hook/startup/proc/createGasOverlays()
+ plmaster = new /obj/effect/overlay()
+ plmaster.icon = 'icons/effects/tile_effects.dmi'
+ plmaster.icon_state = "plasma"
+ plmaster.layer = FLY_LAYER
+ plmaster.mouse_opacity = 0
+
+ slmaster = new /obj/effect/overlay()
+ slmaster.icon = 'icons/effects/tile_effects.dmi'
+ slmaster.icon_state = "sleeping_agent"
+ slmaster.layer = FLY_LAYER
+ slmaster.mouse_opacity = 0
+ return 1
+
+/datum/gas/sleeping_agent/specific_heat = 40 //These are used for the "Trace Gases" stuff, but is buggy.
+
+/datum/gas/oxygen_agent_b/specific_heat = 300
+
+/datum/gas/volatile_fuel/specific_heat = 30
+
+/datum/gas
+ var/moles = 0
+
+ var/specific_heat = 0
+
+ var/moles_archived = 0
+
+/datum/gas_mixture/
+ var/oxygen = 0 //Holds the "moles" of each of the four gases.
+ var/carbon_dioxide = 0
+ var/nitrogen = 0
+ var/toxins = 0
+
+ var/total_moles = 0 //Updated when a reaction occurs.
+
+ var/volume = CELL_VOLUME
+
+ var/temperature = 0 //in Kelvin, use calculate_temperature() to modify
+
+ var/group_multiplier = 1
+ //Size of the group this gas_mixture is representing.
+ //=1 for singletons
+
+ var/graphic
+
+ var/list/datum/gas/trace_gases = list() //Seemed to be a good idea that was abandoned
+
+ var/tmp/oxygen_archived //These are variables for use with the archived data
+ var/tmp/carbon_dioxide_archived
+ var/tmp/nitrogen_archived
+ var/tmp/toxins_archived
+
+ var/tmp/temperature_archived
+
+ var/tmp/graphic_archived = 0
+ var/tmp/fuel_burnt = 0
+
+ var/reacting = 0
+
+//FOR THE LOVE OF GOD PLEASE USE THIS PROC
+//Call it with negative numbers to remove gases.
+
+/datum/gas_mixture/proc/adjust(o2 = 0, co2 = 0, n2 = 0, tx = 0, list/datum/gas/traces = list())
+ //Purpose: Adjusting the gases within a airmix
+ //Called by: Nothing, yet!
+ //Inputs: The values of the gases to adjust
+ //Outputs: null
+
+ oxygen = max(0, oxygen + o2)
+ carbon_dioxide = max(0, carbon_dioxide + co2)
+ nitrogen = max(0, nitrogen + n2)
+ toxins = max(0, toxins + tx)
+
+ //handle trace gasses
+ for(var/datum/gas/G in traces)
+ var/datum/gas/T = locate(G.type) in trace_gases
+ if(T)
+ T.moles = max(G.moles + T.moles, 0)
+ else if(G.moles > 0)
+ trace_gases |= G
+ update_values()
+ return
+
+ //tg seems to like using these a lot
+/datum/gas_mixture/proc/return_temperature()
+ return temperature
+
+
+/datum/gas_mixture/proc/return_volume()
+ return max(0, volume)
+
+
+/datum/gas_mixture/proc/thermal_energy()
+ return temperature*heat_capacity()
+
+///////////////////////////////
+//PV=nRT - related procedures//
+///////////////////////////////
+
+/datum/gas_mixture/proc/heat_capacity()
+ //Purpose: Returning the heat capacity of the gas mix
+ //Called by: UNKNOWN
+ //Inputs: None
+ //Outputs: Heat capacity
+
+ var/heat_capacity = HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins)
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ heat_capacity += trace_gas.moles*trace_gas.specific_heat
+
+ return max(MINIMUM_HEAT_CAPACITY,heat_capacity)
+
+/datum/gas_mixture/proc/heat_capacity_archived()
+ //Purpose: Returning the archived heat capacity of the gas mix
+ //Called by: UNKNOWN
+ //Inputs: None
+ //Outputs: Archived heat capacity
+
+ var/heat_capacity_archived = HEAT_CAPACITY_CALCULATION(oxygen_archived,carbon_dioxide_archived,nitrogen_archived,toxins_archived)
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ heat_capacity_archived += trace_gas.moles_archived*trace_gas.specific_heat
+
+ return max(MINIMUM_HEAT_CAPACITY,heat_capacity_archived)
+
+/datum/gas_mixture/proc/total_moles()
+ return total_moles
+ /*var/moles = oxygen + carbon_dioxide + nitrogen + toxins
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ moles += trace_gas.moles
+ return moles*/
+
+/datum/gas_mixture/proc/return_pressure()
+ //Purpose: Calculating Current Pressure
+ //Called by:
+ //Inputs: None
+ //Outputs: Gas pressure.
+
+ if(volume>0)
+ return total_moles()*R_IDEAL_GAS_EQUATION*temperature/volume
+ return 0
+
+// proc/return_temperature()
+ //Purpose:
+ //Inputs:
+ //Outputs:
+
+// return temperature
+
+// proc/return_volume()
+ //Purpose:
+ //Inputs:
+ //Outputs:
+
+// return max(0, volume)
+
+// proc/thermal_energy()
+ //Purpose:
+ //Inputs:
+ //Outputs:
+
+// return temperature*heat_capacity()
+
+/datum/gas_mixture/proc/update_values()
+ //Purpose: Calculating and storing values which were normally called CONSTANTLY
+ //Called by: Anything that changes values within a gas mix.
+ //Inputs: None
+ //Outputs: None
+
+ total_moles = oxygen + carbon_dioxide + nitrogen + toxins
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ total_moles += trace_gas.moles
+
+ return
+
+////////////////////////////////////////////
+//Procedures used for very specific events//
+////////////////////////////////////////////
+
+/datum/gas_mixture/proc/check_tile_graphic()
+ //Purpose: Calculating the graphic for a tile
+ //Called by: Turfs updating
+ //Inputs: None
+ //Outputs: 1 if graphic changed, 0 if unchanged
+
+ graphic = 0
+ if(toxins > MOLES_PLASMA_VISIBLE)
+ graphic = 1
+ else if(length(trace_gases))
+ var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in trace_gases
+ if(sleeping_agent && (sleeping_agent.moles > 1))
+ graphic = 2
+ else
+ graphic = 0
+
+ return graphic != graphic_archived
+
+/datum/gas_mixture/proc/react(atom/dump_location)
+ //Purpose: Calculating if it is possible for a fire to occur in the airmix
+ //Called by: Air mixes updating?
+ //Inputs: None
+ //Outputs: If a fire occured
+
+ //set to 1 if a notable reaction occured (used by pipe_network)
+
+ zburn(null)
+
+ return reacting
+
+/*
+/datum/gas_mixture/proc/fire()
+ //Purpose: Calculating any fire reactions.
+ //Called by: react() (See above)
+ //Inputs: None
+ //Outputs: How much fuel burned
+
+ return zburn(null)
+
+ var/energy_released = 0
+ var/old_heat_capacity = heat_capacity()
+
+ var/datum/gas/volatile_fuel/fuel_store = locate(/datum/gas/volatile_fuel) in trace_gases
+ if(fuel_store) //General volatile gas burn
+ var/burned_fuel = 0
+
+ if(oxygen < fuel_store.moles)
+ burned_fuel = oxygen
+ fuel_store.moles -= burned_fuel
+ oxygen = 0
+ else
+ burned_fuel = fuel_store.moles
+ oxygen -= fuel_store.moles
+ del(fuel_store)
+
+ energy_released += FIRE_CARBON_ENERGY_RELEASED * burned_fuel
+ carbon_dioxide += burned_fuel
+ fuel_burnt += burned_fuel
+
+ //Handle plasma burning
+ if(toxins > MINIMUM_HEAT_CAPACITY)
+ var/plasma_burn_rate = 0
+ var/oxygen_burn_rate = 0
+ //more plasma released at higher temperatures
+ var/temperature_scale
+ if(temperature > PLASMA_UPPER_TEMPERATURE)
+ temperature_scale = 1
+ else
+ temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE)
+ if(temperature_scale > 0)
+ oxygen_burn_rate = 1.4 - temperature_scale
+ if(oxygen > toxins*PLASMA_OXYGEN_FULLBURN)
+ plasma_burn_rate = (toxins*temperature_scale)/4
+ else
+ plasma_burn_rate = (temperature_scale*(oxygen/PLASMA_OXYGEN_FULLBURN))/4
+ if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
+ toxins -= plasma_burn_rate
+ oxygen -= plasma_burn_rate*oxygen_burn_rate
+ carbon_dioxide += plasma_burn_rate
+
+ energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
+
+ fuel_burnt += (plasma_burn_rate)*(1+oxygen_burn_rate)
+
+ if(energy_released > 0)
+ var/new_heat_capacity = heat_capacity()
+ if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
+ temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity
+ update_values()
+
+ return fuel_burnt*/
+
+//////////////////////////////////////////////
+//Procs for general gas spread calculations.//
+//////////////////////////////////////////////
+
+
+/datum/gas_mixture/proc/archive()
+ //Purpose: Archives the current gas values
+ //Called by: UNKNOWN
+ //Inputs: None
+ //Outputs: 1
+
+ oxygen_archived = oxygen
+ carbon_dioxide_archived = carbon_dioxide
+ nitrogen_archived = nitrogen
+ toxins_archived = toxins
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ trace_gas.moles_archived = trace_gas.moles
+
+ temperature_archived = temperature
+
+ graphic_archived = graphic
+
+ return 1
+
+/datum/gas_mixture/proc/check_then_merge(datum/gas_mixture/giver)
+ //Purpose: Similar to merge(...) but first checks to see if the amount of air assumed is small enough
+ // that group processing is still accurate for source (aborts if not)
+ //Called by: airgroups/machinery expelling air, ?
+ //Inputs: The gas to try and merge
+ //Outputs: 1 on successful merge. 0 otherwise.
+
+ if(!giver)
+ return 0
+ if(((giver.oxygen > MINIMUM_AIR_TO_SUSPEND) && (giver.oxygen >= oxygen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((giver.carbon_dioxide > MINIMUM_AIR_TO_SUSPEND) && (giver.carbon_dioxide >= carbon_dioxide*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((giver.nitrogen > MINIMUM_AIR_TO_SUSPEND) && (giver.nitrogen >= nitrogen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((giver.toxins > MINIMUM_AIR_TO_SUSPEND) && (giver.toxins >= toxins*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ return 0
+ if(abs(giver.temperature - temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return 0
+
+ if(giver.trace_gases.len)
+ for(var/datum/gas/trace_gas in giver.trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if((trace_gas.moles > MINIMUM_AIR_TO_SUSPEND) && (!corresponding || (trace_gas.moles >= corresponding.moles*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ return 0
+
+ return merge(giver)
+
+/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
+ //Purpose: Merges all air from giver into self. Deletes giver.
+ //Called by: Machinery expelling air, check_then_merge, ?
+ //Inputs: The gas to merge.
+ //Outputs: 1
+
+ if(!giver)
+ return 0
+
+ if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity()*group_multiplier
+ var/giver_heat_capacity = giver.heat_capacity()*giver.group_multiplier
+ var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
+ if(combined_heat_capacity != 0)
+ temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity
+
+ if((group_multiplier>1)||(giver.group_multiplier>1))
+ oxygen += giver.oxygen*giver.group_multiplier/group_multiplier
+ carbon_dioxide += giver.carbon_dioxide*giver.group_multiplier/group_multiplier
+ nitrogen += giver.nitrogen*giver.group_multiplier/group_multiplier
+ toxins += giver.toxins*giver.group_multiplier/group_multiplier
+ else
+ oxygen += giver.oxygen
+ carbon_dioxide += giver.carbon_dioxide
+ nitrogen += giver.nitrogen
+ toxins += giver.toxins
+
+ if(giver.trace_gases.len)
+ for(var/datum/gas/trace_gas in giver.trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if(!corresponding)
+ corresponding = new trace_gas.type()
+ trace_gases += corresponding
+ corresponding.moles += trace_gas.moles*giver.group_multiplier/group_multiplier
+ update_values()
+
+ // Let the garbage collector handle it, faster according to /tg/ testers
+ //del(giver)
+ return 1
+
+/datum/gas_mixture/proc/remove(amount)
+ //Purpose: Removes a certain number of moles from the air.
+ //Called by: ?
+ //Inputs: How many moles to remove.
+ //Outputs: Removed air.
+
+ var/sum = total_moles()
+ amount = min(amount,sum) //Can not take more air than tile has!
+ if(amount <= 0)
+ return null
+
+ var/datum/gas_mixture/removed = new
+
+
+ removed.oxygen = QUANTIZE((oxygen/sum)*amount)
+ removed.nitrogen = QUANTIZE((nitrogen/sum)*amount)
+ removed.carbon_dioxide = QUANTIZE((carbon_dioxide/sum)*amount)
+ removed.toxins = QUANTIZE(((toxins/sum)*amount))
+
+ oxygen -= removed.oxygen/group_multiplier
+ nitrogen -= removed.nitrogen/group_multiplier
+ carbon_dioxide -= removed.carbon_dioxide/group_multiplier
+ toxins -= removed.toxins/group_multiplier
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ var/datum/gas/corresponding = new trace_gas.type()
+ removed.trace_gases += corresponding
+
+ corresponding.moles = ((trace_gas.moles/sum)*amount)
+ trace_gas.moles -= (corresponding.moles/group_multiplier)
+
+ removed.temperature = temperature
+ update_values()
+ removed.update_values()
+
+ return removed
+
+/datum/gas_mixture/proc/remove_ratio(ratio)
+ //Purpose: Removes a certain ratio of the air.
+ //Called by: ?
+ //Inputs: Percentage to remove.
+ //Outputs: Removed air.
+
+ if(ratio <= 0)
+ return null
+
+ ratio = min(ratio, 1)
+
+ var/datum/gas_mixture/removed = new
+
+ removed.oxygen = QUANTIZE(oxygen*ratio)
+ removed.nitrogen = QUANTIZE(nitrogen*ratio)
+ removed.carbon_dioxide = QUANTIZE(carbon_dioxide*ratio)
+ removed.toxins = QUANTIZE(toxins*ratio)
+
+ oxygen -= removed.oxygen/group_multiplier
+ nitrogen -= removed.nitrogen/group_multiplier
+ carbon_dioxide -= removed.carbon_dioxide/group_multiplier
+ toxins -= removed.toxins/group_multiplier
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ var/datum/gas/corresponding = new trace_gas.type()
+ removed.trace_gases += corresponding
+
+ corresponding.moles = trace_gas.moles*ratio
+ trace_gas.moles -= corresponding.moles/group_multiplier
+
+ removed.temperature = temperature
+ update_values()
+ removed.update_values()
+
+ return removed
+
+/datum/gas_mixture/proc/check_then_remove(amount)
+ //Purpose: Similar to remove(...) but first checks to see if the amount of air removed is small enough
+ // that group processing is still accurate for source (aborts if not)
+ //Called by: ?
+ //Inputs: Number of moles to remove
+ //Outputs: Removed air or 0 if it can remove air or not.
+
+ amount = min(amount,total_moles()) //Can not take more air than tile has!
+
+ if((amount > MINIMUM_AIR_RATIO_TO_SUSPEND) && (amount > total_moles()*MINIMUM_AIR_RATIO_TO_SUSPEND))
+ return 0
+
+ return remove(amount)
+
+/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample)
+ //Purpose: Duplicates the sample air mixture.
+ //Called by: airgroups splitting, ?
+ //Inputs: Gas to copy
+ //Outputs: 1
+
+ oxygen = sample.oxygen
+ carbon_dioxide = sample.carbon_dioxide
+ nitrogen = sample.nitrogen
+ toxins = sample.toxins
+ total_moles = sample.total_moles()
+
+ trace_gases.len=null
+ if(sample.trace_gases.len > 0)
+ for(var/datum/gas/trace_gas in sample.trace_gases)
+ var/datum/gas/corresponding = new trace_gas.type()
+ trace_gases += corresponding
+
+ corresponding.moles = trace_gas.moles
+
+ temperature = sample.temperature
+
+ return 1
+
+/datum/gas_mixture/proc/check_gas_mixture(datum/gas_mixture/sharer)
+ //Purpose: Telling if one or both airgroups needs to disable group processing.
+ //Called by: Airgroups sharing air, checking if group processing needs disabled.
+ //Inputs: Gas to compare from other airgroup
+ //Outputs: 0 if the self-check failed (local airgroup breaks?)
+ // then -1 if sharer-check failed (sharing airgroup breaks?)
+ // then 1 if both checks pass (share succesful?)
+ if(!istype(sharer))
+ return
+
+ var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION
+ var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION
+ var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION
+ var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION
+
+ var/delta_temperature = (temperature_archived - sharer.temperature_archived)
+
+ if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ return 0
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return 0
+
+ if(sharer.trace_gases.len)
+ for(var/datum/gas/trace_gas in sharer.trace_gases)
+ if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if(corresponding)
+ if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4)
+ return 0
+ else
+ return 0
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4)
+ if(!locate(trace_gas.type) in sharer.trace_gases)
+ return 0
+
+ if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= sharer.oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= sharer.carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= sharer.nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= sharer.toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ return -1
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4)
+ var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases
+ if(corresponding)
+ if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4)
+ return -1
+ else
+ return -1
+
+ return 1
+
+/datum/gas_mixture/proc/check_turf(turf/model)
+ //Purpose: Used to compare the gases in an unsimulated turf with the gas in a simulated one.
+ //Called by: Sharing air (mimicing) with adjacent unsimulated turfs
+ //Inputs: Unsimulated turf
+ //Outputs: 1 if safe to mimic, 0 if needs to break airgroup.
+
+ var/delta_oxygen = (oxygen_archived - model.oxygen)/TRANSFER_FRACTION
+ var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION
+ var/delta_nitrogen = (nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION
+ var/delta_toxins = (toxins_archived - model.toxins)/TRANSFER_FRACTION
+
+ var/delta_temperature = (temperature_archived - model.temperature)
+
+ if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ return 0
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return 0
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4)
+ return 0
+
+ return 1
+
+/datum/gas_mixture/proc/share(datum/gas_mixture/sharer)
+ //Purpose: Used to transfer gas from a more pressurised tile to a less presurised tile
+ // (Two directional, if the other tile is more pressurised, air travels to current tile)
+ //Called by: Sharing air with adjacent simulated turfs
+ //Inputs: Air datum to share with
+ //Outputs: Amount of gas exchanged (Negative if lost air, positive if gained.)
+
+
+ if(!istype(sharer))
+ return
+
+ var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION
+ var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION
+ var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION
+ var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION
+
+ var/delta_temperature = (temperature_archived - sharer.temperature_archived)
+
+ var/old_self_heat_capacity = 0
+ var/old_sharer_heat_capacity = 0
+
+ var/heat_self_to_sharer = 0
+ var/heat_capacity_self_to_sharer = 0
+ var/heat_sharer_to_self = 0
+ var/heat_capacity_sharer_to_self = 0
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+
+ var/delta_air = delta_oxygen+delta_nitrogen
+ if(delta_air)
+ var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air
+ if(delta_air > 0)
+ heat_self_to_sharer += air_heat_capacity*temperature_archived
+ heat_capacity_self_to_sharer += air_heat_capacity
+ else
+ heat_sharer_to_self -= air_heat_capacity*sharer.temperature_archived
+ heat_capacity_sharer_to_self -= air_heat_capacity
+
+ if(delta_carbon_dioxide)
+ var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide
+ if(delta_carbon_dioxide > 0)
+ heat_self_to_sharer += carbon_dioxide_heat_capacity*temperature_archived
+ heat_capacity_self_to_sharer += carbon_dioxide_heat_capacity
+ else
+ heat_sharer_to_self -= carbon_dioxide_heat_capacity*sharer.temperature_archived
+ heat_capacity_sharer_to_self -= carbon_dioxide_heat_capacity
+
+ if(delta_toxins)
+ var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins
+ if(delta_toxins > 0)
+ heat_self_to_sharer += toxins_heat_capacity*temperature_archived
+ heat_capacity_self_to_sharer += toxins_heat_capacity
+ else
+ heat_sharer_to_self -= toxins_heat_capacity*sharer.temperature_archived
+ heat_capacity_sharer_to_self -= toxins_heat_capacity
+
+ old_self_heat_capacity = heat_capacity()*group_multiplier
+ old_sharer_heat_capacity = sharer.heat_capacity()*sharer.group_multiplier
+
+ oxygen -= delta_oxygen/group_multiplier
+ sharer.oxygen += delta_oxygen/sharer.group_multiplier
+
+ carbon_dioxide -= delta_carbon_dioxide/group_multiplier
+ sharer.carbon_dioxide += delta_carbon_dioxide/sharer.group_multiplier
+
+ nitrogen -= delta_nitrogen/group_multiplier
+ sharer.nitrogen += delta_nitrogen/sharer.group_multiplier
+
+ toxins -= delta_toxins/group_multiplier
+ sharer.toxins += delta_toxins/sharer.group_multiplier
+
+ var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins)
+
+ var/list/trace_types_considered = list()
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+
+ var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases
+ var/delta = 0
+
+ if(corresponding)
+ delta = QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)/TRANSFER_FRACTION
+ else
+ corresponding = new trace_gas.type()
+ sharer.trace_gases += corresponding
+
+ delta = trace_gas.moles_archived/TRANSFER_FRACTION
+
+ trace_gas.moles -= delta/group_multiplier
+ corresponding.moles += delta/sharer.group_multiplier
+
+ if(delta)
+ var/individual_heat_capacity = trace_gas.specific_heat*delta
+ if(delta > 0)
+ heat_self_to_sharer += individual_heat_capacity*temperature_archived
+ heat_capacity_self_to_sharer += individual_heat_capacity
+ else
+ heat_sharer_to_self -= individual_heat_capacity*sharer.temperature_archived
+ heat_capacity_sharer_to_self -= individual_heat_capacity
+
+ moved_moles += delta
+
+ trace_types_considered += trace_gas.type
+
+
+ if(sharer.trace_gases.len)
+ for(var/datum/gas/trace_gas in sharer.trace_gases)
+ if(trace_gas.type in trace_types_considered) continue
+ else
+ var/datum/gas/corresponding
+ var/delta = 0
+
+ corresponding = new trace_gas.type()
+ trace_gases += corresponding
+
+ delta = trace_gas.moles_archived/TRANSFER_FRACTION
+
+ trace_gas.moles -= delta/sharer.group_multiplier
+ corresponding.moles += delta/group_multiplier
+
+ //Guaranteed transfer from sharer to self
+ var/individual_heat_capacity = trace_gas.specific_heat*delta
+ heat_sharer_to_self += individual_heat_capacity*sharer.temperature_archived
+ heat_capacity_sharer_to_self += individual_heat_capacity
+
+ moved_moles += -delta
+ update_values()
+ sharer.update_values()
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/new_self_heat_capacity = old_self_heat_capacity + heat_capacity_sharer_to_self - heat_capacity_self_to_sharer
+ var/new_sharer_heat_capacity = old_sharer_heat_capacity + heat_capacity_self_to_sharer - heat_capacity_sharer_to_self
+
+ if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY)
+ temperature = (old_self_heat_capacity*temperature - heat_capacity_self_to_sharer*temperature_archived + heat_capacity_sharer_to_self*sharer.temperature_archived)/new_self_heat_capacity
+
+ if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY)
+ sharer.temperature = (old_sharer_heat_capacity*sharer.temperature-heat_capacity_sharer_to_self*sharer.temperature_archived + heat_capacity_self_to_sharer*temperature_archived)/new_sharer_heat_capacity
+
+ if(abs(old_sharer_heat_capacity) > MINIMUM_HEAT_CAPACITY)
+ if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity
+ temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT)
+
+ if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
+ var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - sharer.temperature_archived*(sharer.total_moles() - moved_moles)
+ return delta_pressure*R_IDEAL_GAS_EQUATION/volume
+
+ else
+ return 0
+
+/datum/gas_mixture/proc/mimic(turf/model, border_multiplier)
+ //Purpose: Used transfer gas from a more pressurised tile to a less presurised unsimulated tile.
+ //Called by: "sharing" from unsimulated to simulated turfs.
+ //Inputs: Unsimulated turf, Multiplier for gas transfer (optional)
+ //Outputs: Amount of gas exchanged
+
+ var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen)/TRANSFER_FRACTION
+ var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION
+ var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION
+ var/delta_toxins = QUANTIZE(toxins_archived - model.toxins)/TRANSFER_FRACTION
+
+ var/delta_temperature = (temperature_archived - model.temperature)
+
+ var/heat_transferred = 0
+ var/old_self_heat_capacity = 0
+ var/heat_capacity_transferred = 0
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+
+ var/delta_air = delta_oxygen+delta_nitrogen
+ if(delta_air)
+ var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air
+ heat_transferred -= air_heat_capacity*model.temperature
+ heat_capacity_transferred -= air_heat_capacity
+
+ if(delta_carbon_dioxide)
+ var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide
+ heat_transferred -= carbon_dioxide_heat_capacity*model.temperature
+ heat_capacity_transferred -= carbon_dioxide_heat_capacity
+
+ if(delta_toxins)
+ var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins
+ heat_transferred -= toxins_heat_capacity*model.temperature
+ heat_capacity_transferred -= toxins_heat_capacity
+
+ old_self_heat_capacity = heat_capacity()*group_multiplier
+
+ if(border_multiplier)
+ oxygen -= delta_oxygen*border_multiplier/group_multiplier
+ carbon_dioxide -= delta_carbon_dioxide*border_multiplier/group_multiplier
+ nitrogen -= delta_nitrogen*border_multiplier/group_multiplier
+ toxins -= delta_toxins*border_multiplier/group_multiplier
+ else
+ oxygen -= delta_oxygen/group_multiplier
+ carbon_dioxide -= delta_carbon_dioxide/group_multiplier
+ nitrogen -= delta_nitrogen/group_multiplier
+ toxins -= delta_toxins/group_multiplier
+
+ var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins)
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ var/delta = 0
+
+ delta = trace_gas.moles_archived/TRANSFER_FRACTION
+
+ if(border_multiplier)
+ trace_gas.moles -= delta*border_multiplier/group_multiplier
+ else
+ trace_gas.moles -= delta/group_multiplier
+
+ var/heat_cap_transferred = delta*trace_gas.specific_heat
+ heat_transferred += heat_cap_transferred*temperature_archived
+ heat_capacity_transferred += heat_cap_transferred
+ moved_moles += delta
+ update_values()
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/new_self_heat_capacity = old_self_heat_capacity - heat_capacity_transferred
+ if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY)
+ if(border_multiplier)
+ temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity
+ else
+ temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity
+
+ temperature_mimic(model, model.thermal_conductivity, border_multiplier)
+
+ if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
+ var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - model.temperature*(model.oxygen+model.carbon_dioxide+model.nitrogen+model.toxins)
+ return delta_pressure*R_IDEAL_GAS_EQUATION/volume
+ else
+ return 0
+
+/datum/gas_mixture/proc/check_both_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
+ var/delta_temperature = (temperature_archived - sharer.temperature_archived)
+
+ var/self_heat_capacity = heat_capacity_archived()
+ var/sharer_heat_capacity = sharer.heat_capacity_archived()
+
+ var/self_temperature_delta = 0
+ var/sharer_temperature_delta = 0
+
+ if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity))
+
+ self_temperature_delta = -heat/(self_heat_capacity*group_multiplier)
+ sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier)
+ else
+ return 1
+
+ if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \
+ && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived))
+ return 0
+
+ if((abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \
+ && (abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*sharer.temperature_archived))
+ return -1
+
+ temperature += self_temperature_delta
+ sharer.temperature += sharer_temperature_delta
+
+ return 1
+ //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency
+
+/datum/gas_mixture/proc/check_me_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
+ var/delta_temperature = (temperature_archived - sharer.temperature_archived)
+
+ var/self_heat_capacity = heat_capacity_archived()
+ var/sharer_heat_capacity = sharer.heat_capacity_archived()
+
+ var/self_temperature_delta = 0
+ var/sharer_temperature_delta = 0
+
+ if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity))
+
+ self_temperature_delta = -heat/(self_heat_capacity*group_multiplier)
+ sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier)
+ else
+ return 1
+
+ if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \
+ && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived))
+ return 0
+
+ temperature += self_temperature_delta
+ sharer.temperature += sharer_temperature_delta
+
+ return 1
+ //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency
+
+/datum/gas_mixture/proc/check_me_then_temperature_turf_share(turf/simulated/sharer, conduction_coefficient)
+ var/delta_temperature = (temperature_archived - sharer.temperature)
+
+ var/self_temperature_delta = 0
+ var/sharer_temperature_delta = 0
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity_archived()
+
+ if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity))
+
+ self_temperature_delta = -heat/(self_heat_capacity*group_multiplier)
+ sharer_temperature_delta = heat/sharer.heat_capacity
+ else
+ return 1
+
+ if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \
+ && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived))
+ return 0
+
+ temperature += self_temperature_delta
+ sharer.temperature += sharer_temperature_delta
+
+ return 1
+ //Logic integrated from: temperature_turf_share(sharer, conduction_coefficient) for efficiency
+
+/datum/gas_mixture/proc/check_me_then_temperature_mimic(turf/model, conduction_coefficient)
+ var/delta_temperature = (temperature_archived - model.temperature)
+ var/self_temperature_delta = 0
+
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity_archived()
+
+ if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity))
+
+ self_temperature_delta = -heat/(self_heat_capacity*group_multiplier)
+
+ if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \
+ && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived))
+ return 0
+
+ temperature += self_temperature_delta
+
+ return 1
+ //Logic integrated from: temperature_mimic(model, conduction_coefficient) for efficiency
+
+/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
+ var/delta_temperature = (temperature_archived - sharer.temperature_archived)
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity_archived()
+ var/sharer_heat_capacity = sharer.heat_capacity_archived()
+ if(!group_multiplier)
+ message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!")
+ return
+
+ if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity))
+
+ temperature -= heat/(self_heat_capacity*group_multiplier)
+ sharer.temperature += heat/(sharer_heat_capacity*sharer.group_multiplier)
+
+/datum/gas_mixture/proc/temperature_mimic(turf/model, conduction_coefficient, border_multiplier)
+ var/delta_temperature = (temperature - model.temperature)
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity()//_archived()
+ if(!group_multiplier)
+ message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!")
+ return
+
+ if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity))
+
+ if(border_multiplier)
+ temperature -= heat*border_multiplier/(self_heat_capacity*group_multiplier)
+ else
+ temperature -= heat/(self_heat_capacity*group_multiplier)
+
+/datum/gas_mixture/proc/temperature_turf_share(turf/simulated/sharer, conduction_coefficient)
+ var/delta_temperature = (temperature_archived - sharer.temperature)
+ if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity()
+
+ if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
+ var/heat = conduction_coefficient*delta_temperature* \
+ (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity))
+
+ temperature -= heat/(self_heat_capacity*group_multiplier)
+ sharer.temperature += heat/sharer.heat_capacity
+
+/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
+ //Purpose: Compares sample to self to see if within acceptable ranges that group processing may be enabled
+ //Called by: Airgroups trying to rebuild
+ //Inputs: Gas mix to compare
+ //Outputs: 1 if can rebuild, 0 if not.
+ if(!sample) return 0
+
+
+ if((abs(oxygen-sample.oxygen) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((oxygen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen)))
+ return 0
+ if((abs(nitrogen-sample.nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((nitrogen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen) || (nitrogen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen)))
+ return 0
+ if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (carbon_dioxide > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide)))
+ return 0
+ if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins)))
+ return 0
+
+
+ if(total_moles() > MINIMUM_AIR_TO_SUSPEND)
+ if((abs(temperature-sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
+ ((temperature < (1-MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature) || (temperature > (1+MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature)))
+ //world << "temp fail [temperature] & [sample.temperature]"
+ return 0
+ var/check_moles
+ if(sample.trace_gases.len)
+ for(var/datum/gas/trace_gas in sample.trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if(corresponding)
+ check_moles = corresponding.moles
+ else
+ check_moles = 0
+
+ if((abs(trace_gas.moles - check_moles) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((check_moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles) || (check_moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles)))
+ return 0
+
+ if(trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if(corresponding)
+ check_moles = corresponding.moles
+ else
+ check_moles = 0
+
+ if((abs(trace_gas.moles - check_moles) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((trace_gas.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*check_moles) || (trace_gas.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*check_moles)))
+ return 0
+
+ return 1
+
+/datum/gas_mixture/proc/add(datum/gas_mixture/right_side)
+ oxygen += right_side.oxygen
+ carbon_dioxide += right_side.carbon_dioxide
+ nitrogen += right_side.nitrogen
+ toxins += right_side.toxins
+
+ if(trace_gases.len || right_side.trace_gases.len)
+ for(var/datum/gas/trace_gas in right_side.trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if(!corresponding)
+ corresponding = new trace_gas.type()
+ trace_gases += corresponding
+ corresponding.moles += trace_gas.moles
+
+ update_values()
+ return 1
+
+/datum/gas_mixture/proc/subtract(datum/gas_mixture/right_side)
+ //Purpose: Subtracts right_side from air_mixture. Used to help turfs mingle
+ //Called by: Pipelines ending in a break (or something)
+ //Inputs: Gas mix to remove
+ //Outputs: 1
+
+ oxygen = max(oxygen - right_side.oxygen)
+ carbon_dioxide = max(carbon_dioxide - right_side.carbon_dioxide)
+ nitrogen = max(nitrogen - right_side.nitrogen)
+ toxins = max(toxins - right_side.toxins)
+
+ if(trace_gases.len || right_side.trace_gases.len)
+ for(var/datum/gas/trace_gas in right_side.trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
+ if(corresponding)
+ corresponding.moles = max(0, corresponding.moles - trace_gas.moles)
+
+ update_values()
+ return 1
+
+/datum/gas_mixture/proc/multiply(factor)
+ oxygen *= factor
+ carbon_dioxide *= factor
+ nitrogen *= factor
+ toxins *= factor
+
+ if(trace_gases && trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ trace_gas.moles *= factor
+
+ update_values()
+ return 1
+
+/datum/gas_mixture/proc/divide(factor)
+ oxygen /= factor
+ carbon_dioxide /= factor
+ nitrogen /= factor
+ toxins /= factor
+
+ if(trace_gases && trace_gases.len)
+ for(var/datum/gas/trace_gas in trace_gases)
+ trace_gas.moles /= factor
+
+ update_values()
+ return 1
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index c373e5c9498..81db855b15d 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -9,6 +9,12 @@
src:Topic(href, href_list)
return null
+/proc/get_area_master(O)
+ var/area/A = get_area(O)
+ if(A && A.master)
+ A = A.master
+ return A
+
/proc/get_area(O)
var/atom/location = O
var/i
@@ -161,6 +167,8 @@
// The old system would loop through lists for a total of 5000 per function call, in an empty server.
// This new system will loop at around 1000 in an empty server.
+// SCREW THAT SHIT, we're not recursing.
+
/proc/get_mobs_in_view(var/R, var/atom/source)
// Returns a list of mobs in range of R from source. Used in radio and say code.
@@ -172,18 +180,33 @@
var/list/range = hear(R, T)
- for(var/atom/A in range)
- if(ismob(A))
- var/mob/M = A
- if(M.client)
- hear += M
- //world.log << "Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])"
- else if(istype(A, /obj/item/device/radio))
- hear += A
+ for(var/mob/M in range)
+ hear += M
- if(isobj(A) || ismob(A))
- hear |= recursive_mob_check(A, hear, 3, 1, 0, 1)
+ var/list/objects = list()
+ for(var/obj/O in range) //Get a list of objects in hearing range. We'll check to see if any clients have their "eye" set to the object
+ objects += O
+
+ for(var/client/C in clients)
+ if(!istype(C) || !C.eye)
+ continue //I have no idea when this client check would be needed, but if this runtimes people won't hear anything
+ //So kinda paranoid about runtime avoidance.
+ if(istype(C.eye, /obj/machinery/camera))
+ continue //No microphones in cameras.
+
+ if(C.mob in hear)
+ continue
+
+ var/list/hear_and_objects = (hear|objects) //Combined these lists here instead of doing the combine 3 more times.
+
+ if(C.eye in hear_and_objects)
+ hear += C.mob
+
+ else if(C.mob.loc in hear_and_objects)
+ hear += C.mob
+ else if(C.mob.loc.loc in hear_and_objects)
+ hear += C.mob
return hear
@@ -275,6 +298,12 @@ proc/isInSight(var/atom/A, var/atom/B)
else
return get_step(start, EAST)
+/proc/try_move_adjacent(atom/movable/AM)
+ var/turf/T = get_turf(AM)
+ for(var/direction in cardinal)
+ if(AM.Move(get_step(T, direction)))
+ break
+
/proc/get_mob_by_key(var/key)
for(var/mob/M in mob_list)
if(M.ckey == lowertext(key))
@@ -352,4 +381,69 @@ proc/get_candidates(be_special_flag=0)
C.images += I
sleep(duration)
for(var/client/C in show_to)
- C.images -= I
+ C.images -= I
+
+/proc/get_active_player_count()
+ // Get active players who are playing in the round
+ var/active_players = 0
+ for(var/i = 1; i <= player_list.len; i++)
+ var/mob/M = player_list[i]
+ if(M && M.client)
+ if(istype(M, /mob/new_player)) // exclude people in the lobby
+ continue
+ else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
+ var/mob/dead/observer/O = M
+ if(O.started_as_observer) // Exclude people who started as observers
+ continue
+ active_players++
+ return active_players
+
+/datum/projectile_data
+ var/src_x
+ var/src_y
+ var/time
+ var/distance
+ var/power_x
+ var/power_y
+ var/dest_x
+ var/dest_y
+
+/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
+ var/power_x, var/power_y, var/dest_x, var/dest_y)
+ src.src_x = src_x
+ src.src_y = src_y
+ src.time = time
+ src.distance = distance
+ src.power_x = power_x
+ src.power_y = power_y
+ src.dest_x = dest_x
+ src.dest_y = dest_y
+
+/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power)
+
+ // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle],
+ // rotated at [rotation] and with the power of [power]
+ // Thanks to VistaPOWA for this function
+
+ var/power_x = power * cos(angle)
+ var/power_y = power * sin(angle)
+ var/time = 2* power_y / 10 //10 = g
+
+ var/distance = time * power_x
+
+ var/dest_x = src_x + distance*sin(rotation);
+ var/dest_y = src_y + distance*cos(rotation);
+
+ return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
+
+
+/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=mob_list)
+ var/list/mobs_found[0]
+ var/area/our_area = get_area_master(the_area)
+ for(var/mob/M in moblist)
+ if(client_needed && !M.client)
+ continue
+ if(our_area != get_area_master(M))
+ continue
+ mobs_found += M
+ return mobs_found
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 0805f639c4d..5cbba11b197 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -19,7 +19,8 @@ var/global/list/landmarks_list = list() //list of all landmarks created
var/global/list/surgery_steps = list() //list of all surgery steps |BS12
var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12
var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking.
-var/global/list/table_recipes = list() //list of all table craft recipes
+var/global/list/table_recipes = list() //list of all table craft recipes
+var/global/list/joblist = list() //list of all jobstypes, minus borg and AI
//Languages/species/whitelist.
var/global/list/all_species[0]
@@ -88,6 +89,11 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al
var/datum/medical_effect/M = new T
side_effects[M.name] = T
+ //List of job. I can't believe this was calculated multiple times per tick!
+ paths = typesof(/datum/job) -list(/datum/job,/datum/job/ai,/datum/job/cyborg)
+ for(var/T in paths)
+ var/datum/job/J = new T
+ joblist[J.title] = J
//Languages and species.
paths = typesof(/datum/language)-/datum/language
@@ -125,4 +131,4 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al
for(var/path in typesof(prototype))
if(path == prototype) continue
L += new path()
- return L
+ return L
diff --git a/code/__HELPERS/loc.dm b/code/__HELPERS/loc.dm
new file mode 100644
index 00000000000..91be708854d
--- /dev/null
+++ b/code/__HELPERS/loc.dm
@@ -0,0 +1,7 @@
+atom/movable/proc/setloc(newloc)
+ if(light)
+ var/oldloc = loc
+ loc = newloc
+ if(isturf(newloc) != isturf(oldloc)) light.Reset()
+ else
+ loc = newloc
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 4d8eca375e8..19e1562b183 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -76,7 +76,6 @@ proc/age2agedescription(age)
if(70 to INFINITY) return "elderly"
else return "unknown"
-
/*
Proc for attack log creation, because really why not
1 argument is the actor
@@ -95,3 +94,26 @@ proc/add_logs(mob/target, mob/user, what_done, var/object=null, var/addition=nul
if(target.client)
if(what_done != ("shaked" || "CPRed" || "grabbed"))
message_admins("[user.name][ismob(user) ? "([user.ckey])" : ""] [what_done] [target.name][ismob(target) ? "([target.ckey])" : ""][object ? " with [object]" : " "][addition](JMP)")
+
+proc/RoundHealth(health)
+ switch(health)
+ if(100 to INFINITY)
+ return "health100"
+ if(70 to 100)
+ return "health80"
+ if(50 to 70)
+ return "health60"
+ if(30 to 50)
+ return "health40"
+ if(18 to 30)
+ return "health25"
+ if(5 to 18)
+ return "health10"
+ if(1 to 5)
+ return "health1"
+ if(-99 to 0)
+ return "health0"
+ else
+ return "health-100"
+ return "0"
+
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index 9f2ac483290..d2a82a034cf 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -206,7 +206,7 @@ var/syndicate_code_response//Code response for traitors.
code_phrase += " "
code_phrase += pick(last_names)
if(2)
- code_phrase += pick(get_all_jobs())//Returns a job.
+ code_phrase += pick(joblist)//Returns a job.
safety -= 1
if(2)
switch(rand(1,2))//Places or things.
@@ -283,7 +283,7 @@ var/syndicate_code_response//Code response for traitors.
if(5)
syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's")
syndicate_code_phrase += " "
- syndicate_code_phrase += "[pick(get_all_jobs())]"
+ syndicate_code_phrase += "[pick(joblist)]"
syndicate_code_phrase += "?"
switch(choice)
@@ -312,7 +312,7 @@ var/syndicate_code_response//Code response for traitors.
syndicate_code_response += pick(last_names)
else
syndicate_code_response += " the "
- syndicate_code_response += "[pic(get_all_jobs())]"
+ syndicate_code_response += "[pic(joblist)]"
syndicate_code_response += "."
else
syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*")
diff --git a/code/_hooks/area.dm b/code/_hooks/area.dm
new file mode 100644
index 00000000000..6b2834af165
--- /dev/null
+++ b/code/_hooks/area.dm
@@ -0,0 +1,4 @@
+// Called when a mob moves from one MASTER area to another.
+// (Master, as opposed to lighting subareas)
+/hook/mobAreaChange
+ name = "MobAreaChange"
\ No newline at end of file
diff --git a/code/_hooks/hooks.dm b/code/_hooks/hooks.dm
new file mode 100644
index 00000000000..51aea802165
--- /dev/null
+++ b/code/_hooks/hooks.dm
@@ -0,0 +1,54 @@
+/************
+ * D2K5-STYLE HOOKS
+ *
+ * BLATANTLY STOLEN FROM D2K5 AND MODIFIED
+ *
+ * SOMEHOW SUCKS LESS THAN BAY'S HOOKS
+ ************
+
+ The major change is to standardize them a bit
+ by changing the event prefix to On instead of Hook.
+
+ Oh and it's documented and cleaned up. - N3X
+ */
+
+/hook
+ var/name = "DefaultHookName"
+ var/list/handlers = list()
+
+ proc/Called(var/list/args) // When the hook is called
+ return 0
+
+ proc/Setup() // Called when the setup things is ran for the hook, objs contain all objects with that is hooking
+
+/hook_handler
+ // Your hook handler should do this:
+ // proc/OnThingHappened(var/list/args)
+ // return handled // boolean
+
+var/global/list/hooks = list()
+
+/proc/SetupHooks()
+ for (var/hook_path in typesof(/hook))
+ var/hook/hook = new hook_path
+ hooks[hook.name] = hook
+ world.log << "Found hook: " + hook.name
+ for (var/hook_path in typesof(/hook_handler))
+ var/hook_handler/hook_handler = new hook_path
+ for (var/name in hooks)
+ if (hascall(hook_handler, "On" + name))
+ var/hook/hook = hooks[name]
+ hook.handlers += hook_handler
+ world.log << "Found hook handler for: " + name
+ for (var/hook/hook in hooks)
+ hook.Setup()
+
+/proc/CallHook(var/name as text, var/list/args)
+ var/hook/hook = hooks[name]
+ if (!hook)
+ //world.log << "WARNING: Hook with name " + name + " does not exist"
+ return
+ if (hook.Called(args))
+ return
+ for (var/hook_handler/hook_handler in hook.handlers)
+ call(hook_handler, "On" + hook.name)(args)
\ No newline at end of file
diff --git a/code/_hooks/mob.dm b/code/_hooks/mob.dm
new file mode 100644
index 00000000000..f0ea2952f57
--- /dev/null
+++ b/code/_hooks/mob.dm
@@ -0,0 +1,2 @@
+/hook/login
+ name = "Login"
\ No newline at end of file
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index 112c3431e52..29910ceaea9 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -32,6 +32,8 @@
build_click(src, client.buildmode, params, A)
return
+ if(alienAI) return
+
var/list/modifiers = params2list(params)
if(modifiers["middle"])
MiddleClickOn(A)
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index d86faacbd1f..d1f5f439c71 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -15,6 +15,9 @@
build_click(src, client.buildmode, params, A)
return
+ if(stat || lockcharge || weakened || stunned || paralysis)
+ return
+
var/list/modifiers = params2list(params)
if(modifiers["middle"])
MiddleClickOn(A)
@@ -29,9 +32,6 @@
CtrlClickOn(A)
return
- if(stat || lockcharge || weakened || stunned || paralysis)
- return
-
if(next_move >= world.time)
return
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 33289c34591..e1a89c454ce 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -145,7 +145,7 @@
if(istype(M, /mob/living/carbon/human))
- M:attacked_by(src, user, def_zone)
+ return M:attacked_by(src, user, def_zone)
else
switch(damtype)
if("brute")
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 23d422b538b..1fd31a5bd18 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -1,15 +1,3 @@
-/client/var/inquisitive_ghost = 1
-/mob/dead/observer/verb/toggle_inquisition() // warning: unexpected inquisition
- set name = "Toggle Inquisitiveness"
- set desc = "Sets whether your ghost examines everything on click by default"
- set category = "Ghost"
- if(!client) return
- client.inquisitive_ghost = !client.inquisitive_ghost
- if(client.inquisitive_ghost)
- src << "\blue You will now examine everything you click on."
- else
- src << "\blue You will no longer examine things you click on."
-
/mob/dead/observer/DblClickOn(var/atom/A, var/params)
if(client.buildmode)
build_click(src, client.buildmode, params, A)
@@ -33,10 +21,16 @@
return
if(world.time <= next_move) return
next_move = world.time + 8
+
+ var/list/modifiers = params2list(params)
+ if(modifiers["shift"])
+ ShiftClickOn(A)
+ return
// You are responsible for checking config.ghost_interaction when you override this function
// Not all of them require checking, see below
A.attack_ghost(src)
+
// This is the ghost's follow verb with an argument
/mob/dead/observer/proc/ManualFollow(var/atom/target)
following = target
@@ -58,11 +52,12 @@
sleep(15)
following = null
-// Oh by the way this didn't work with old click code which is why clicking shit didn't spam you
-/atom/proc/attack_ghost(mob/dead/observer/user as mob)
- if(user.client && user.client.inquisitive_ghost)
- examine()
- return
+// We don't need a fucking toggle.
+/mob/dead/observer/ShiftClickOn(var/atom/A)
+ A.examine()
+
+/atom/proc/attack_ghost(mob/user as mob)
+ src.examine()
// ---------------------------------------
// And here are some good things for free:
diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm
new file mode 100644
index 00000000000..91f24465569
--- /dev/null
+++ b/code/_onclick/overmind.dm
@@ -0,0 +1,17 @@
+// Blob Overmind Controls
+
+
+/mob/camera/blob/CtrlClickOn(var/atom/A) // Expand blob
+ var/turf/T = get_turf(A)
+ if(T)
+ expand_blob(T)
+
+/mob/camera/blob/MiddleClickOn(var/atom/A) // Rally spores
+ var/turf/T = get_turf(A)
+ if(T)
+ rally_spores(T)
+
+/mob/camera/blob/AltClickOn(var/atom/A) // Create a shield
+ var/turf/T = get_turf(A)
+ if(T)
+ create_shield(T)
\ No newline at end of file
diff --git a/code/controllers/_DynamicAreaLighting_TG.dm b/code/controllers/_DynamicAreaLighting_TG.dm
index 5f876207808..fde5b841345 100644
--- a/code/controllers/_DynamicAreaLighting_TG.dm
+++ b/code/controllers/_DynamicAreaLighting_TG.dm
@@ -152,13 +152,13 @@ atom/movable/New()
//Turfs with opacity will trigger nearby lights to update at next lighting process.
//TODO: is this really necessary? Removing it could help reduce lag during singulo-mayhem somewhat
-turf/Del()
+turf/Destroy()
if(opacity)
UpdateAffectingLights()
..()
//Objects with opacity will trigger nearby lights to update at next lighting process.
-atom/movable/Del()
+atom/movable/Destroy()
if(opacity)
UpdateAffectingLights()
..()
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index e8b63a86ae3..8718f7dc12a 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -72,6 +72,8 @@
var/wikiurl = "http://baystation12.net/wiki/index.php?title=Main_Page"
var/forumurl = "http://baystation12.net/forums/"
+ var/media_base_url = "http://80.244.78.90/media" // http://ss13.nexisonline.net/media
+
//Alert level description
var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced."
var/alert_desc_blue_upto = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted."
@@ -450,6 +452,10 @@
if("assistant_ratio")
config.assistantratio = text2num(value)
+
+ if("media_base_url")
+ media_base_url = value
+
else
diary << "Unknown setting in configuration: '[name]'"
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index ff46b6b10b6..f4231464efa 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -45,7 +45,8 @@ var/datum/controller/failsafe/Failsafe
MC_defcon = 0
MC_iteration = controller_iteration
- if(lighting_controller.processing)
+ //Lighting controller now neither processes nor iterates.
+ /*if(lighting_controller.processing)
if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
switch(lighting_defcon)
if(0 to 3)
@@ -60,7 +61,7 @@ var/datum/controller/failsafe/Failsafe
lighting_defcon = 0
else
lighting_defcon = 0
- lighting_iteration = lighting_controller.iteration
+ lighting_iteration = lighting_controller.iteration*/
else
MC_defcon = 0
lighting_defcon = 0
diff --git a/code/controllers/garbage.dm b/code/controllers/garbage.dm
index 1a16906a212..412a4bdef10 100644
--- a/code/controllers/garbage.dm
+++ b/code/controllers/garbage.dm
@@ -1,5 +1,6 @@
-#define GC_COLLECTIONS_PER_TICK 100
+#define GC_COLLECTIONS_PER_TICK 250 // Was 100
+#define GC_COLLECTION_TIMEOUT 100 // 10s
var/global/datum/controller/garbage_collector/garbage
var/global/list/uncollectable_vars=list(
"alpha",
@@ -11,6 +12,7 @@ var/global/list/uncollectable_vars=list(
"color",
"contents",
"gender",
+ "group",
"key",
//"loc",
"locs",
@@ -19,10 +21,12 @@ var/global/list/uncollectable_vars=list(
"parent_type",
"step_size",
"glide_size",
+ "gc_destroyed",
"step_x",
"step_y",
"step_z",
"tag",
+ "thermal_conductivity",
"type",
"vars",
"verbs",
@@ -32,7 +36,9 @@ var/global/list/uncollectable_vars=list(
)
/datum/controller/garbage_collector
var/list/queue=list()
+ var/list/destroyed=list()
var/waiting=0
+ var/del_everything=1
var/turf/trashbin=null
New()
@@ -41,23 +47,39 @@ var/global/list/uncollectable_vars=list(
proc/AddTrash(var/atom/movable/A)
if(!A)
return
+ if(del_everything)
+ del(A)
+ return
A.loc=trashbin
queue.Add(A)
waiting++
proc/Pop()
var/atom/movable/A = queue[1]
- if(!A) return
+ if(!A)
+ if(isnull(A))
+ var/loopcheck = 0
+ while(queue.Remove(null))
+ loopcheck++
+ if(loopcheck > 50)
+ break
+ return
+ if(del_everything)
+ del(A)
+ return
if(!istype(A,/atom/movable))
testing("GC given a [A.type].")
del(A)
return
for(var/vname in A.vars)
+ if(!issaved(A.vars[vname]))
+ continue
if(vname in uncollectable_vars)
continue
//testing("Unsetting [vname] in [A.type]!")
A.vars[vname]=null
A.loc=null
+ destroyed.Add("\ref[A]")
queue.Remove(A)
proc/process()
@@ -65,6 +87,14 @@ var/global/list/uncollectable_vars=list(
if(waiting)
Pop()
waiting--
+ for(var/i=0;i= world.time - GC_COLLECTION_TIMEOUT)
+ // Something's still referring to the qdel'd object. Kill it.
+ del(A)
+ destroyed.Remove(refID)
/**
* NEVER USE THIS FOR ANYTHING OTHER THAN /atom/movable
@@ -73,7 +103,22 @@ var/global/list/uncollectable_vars=list(
/proc/qdel(var/atom/movable/A)
if(!A) return
if(!istype(A))
- warning("qdel passed a [A.type]. qdel() can only handle /atom/movable types.")
+ warning("qdel() passed object of type [A.type]. qdel() can only handle /atom/movable types.")
del(A)
return
- garbage.AddTrash(A)
\ No newline at end of file
+ if(!garbage)
+ del(A)
+ return
+ // Let our friend know they're about to get fucked up.
+ A.Destroy()
+ garbage.AddTrash(A)
+
+/client/proc/qdel_toggle()
+ set name = "Toggle qdel Behavior"
+ set desc = "Toggle qdel usage between normal and force del()."
+ set category = "Debug"
+
+ garbage.del_everything = !garbage.del_everything
+ world << "GC: qdel turned [garbage.del_everything?"off":"on"]."
+ log_admin("[key_name(usr)] turned qdel [garbage.del_everything?"off":"on"].")
+ message_admins("\blue [key_name(usr)] turned qdel [garbage.del_everything?"off":"on"].", 1)
\ No newline at end of file
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index a0121b45133..417c618c8cc 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -83,6 +83,7 @@ datum/controller/game_controller/proc/setup()
if(ticker)
ticker.pregame()
+ new/datum/controller/lighting()
lighting_controller.Initialize()
@@ -266,11 +267,28 @@ datum/controller/game_controller/proc/process_machines()
last_thing_processed = Machine.type
if(Machine.process() != PROCESS_KILL)
if(Machine)
- if(Machine.use_power)
- Machine.auto_use_power()
+
+// if(Machine.use_power)
+// Machine.auto_use_power()
+
i++
continue
machines.Cut(i,i+1)
+ i=1
+ while(i<=active_areas.len)
+ var/area/A = active_areas[i]
+ if(A.powerupdate)
+ A.powerupdate -= 1
+ for(var/obj/machinery/M in A)
+ if(M)
+ if(M.use_power)
+ M.auto_use_power()
+
+ if(A.apc.len)
+ i++
+ continue
+ active_areas.Cut(i,i+1)
+
datum/controller/game_controller/proc/process_objects()
var/i = 1
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index d659d2fa68c..5d621b1b726 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -19,7 +19,7 @@
feedback_add_details("admin_verb","RFailsafe")
if("Lighting")
new /datum/controller/lighting()
- lighting_controller.process()
+ //lighting_controller.process()
feedback_add_details("admin_verb","RLighting")
if("Supply Shuttle")
supply_shuttle.process()
@@ -28,7 +28,7 @@
return
-/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply Shuttle","Emergency Shuttle","Configuration","pAI", "Cameras"))
+/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply Shuttle","Emergency Shuttle","Configuration","pAI", "Cameras","Garbage"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -47,6 +47,9 @@
if("Lighting")
debug_variables(lighting_controller)
feedback_add_details("admin_verb","DLighting")
+ if("Garbage")
+ debug_variables(garbage)
+ feedback_add_details("admin_verb","DGarbage")
if("Air")
debug_variables(air_master)
feedback_add_details("admin_verb","DAir")
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index f303e07366a..64e629e56a7 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -11,6 +11,8 @@
return
/obj/effect/datacore/proc/manifest_modify(var/name, var/assignment)
+ if(PDA_Manifest.len)
+ PDA_Manifest.Cut()
var/datum/data/record/foundrecord
var/real_title = assignment
@@ -34,6 +36,9 @@
foundrecord.fields["real_rank"] = real_title
/obj/effect/datacore/proc/manifest_inject(var/mob/living/carbon/human/H)
+ if(PDA_Manifest.len)
+ PDA_Manifest.Cut()
+
if(H.mind && (H.mind.assigned_role != "MODE"))
var/assignment
if(H.mind.role_alt_title)
@@ -272,4 +277,4 @@ proc/get_id_photo(var/mob/living/carbon/human/H)
del(eyes_s)
del(clothes_s)
- return preview_icon
\ No newline at end of file
+ return preview_icon
diff --git a/code/datums/disease.dm b/code/datums/disease.dm
index 597dbd9c2b2..6f02043a072 100644
--- a/code/datums/disease.dm
+++ b/code/datums/disease.dm
@@ -204,6 +204,6 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease
return new type(process, src)
/*
-/datum/disease/Del()
+/datum/disease/Destroy()
active_diseases.Remove(src)
*/
diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm
index 0a10a7bd066..14a01866ff4 100644
--- a/code/datums/diseases/advance/symptoms/dizzy.dm
+++ b/code/datums/diseases/advance/symptoms/dizzy.dm
@@ -33,5 +33,5 @@ Bonus
M << "[pick("You feel dizzy.", "Your head starts spinning.")]"
else
M << "You are unable to look straight!"
- M.make_dizzy(5)
+ M.Dizzy(5)
return
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 57701a4ec08..88fade901ab 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -389,7 +389,7 @@ datum/mind
if(!check_rights(R_ADMIN)) return
if (href_list["role_edit"])
- var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs()
+ var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist
if (!new_role) return
assigned_role = new_role
@@ -414,7 +414,7 @@ datum/mind
if(!def_value)//If it's a custom objective, it will be an empty string.
def_value = "custom"
- var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "custom")
+ var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "harm", "speciesist", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "custom")
if (!new_obj_type) return
var/datum/objective/new_objective = null
@@ -452,6 +452,11 @@ datum/mind
//Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops.
new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role=="MODE" ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]."
+ if ("speciesist")
+ new_objective = new /datum/objective/speciesist
+ new_objective.owner = src
+ new_objective.find_target()
+
if ("prevent")
new_objective = new /datum/objective/block
new_objective.owner = src
@@ -538,12 +543,15 @@ datum/mind
else if(href_list["implant"])
var/mob/living/carbon/human/H = current
+
+ H.hud_updateflag |= (1 << IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not.
+
switch(href_list["implant"])
if("remove")
for(var/obj/item/weapon/implant/loyalty/I in H.contents)
for(var/datum/organ/external/organs in H.organs)
if(I in organs.implants)
- I.Del()
+ I.Destroy()
H << "\blue Your loyalty implant has been deactivated."
if("add")
var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
@@ -578,6 +586,8 @@ datum/mind
log_admin("[key_name_admin(usr)] has de-traitor'ed [current].")
else if (href_list["revolution"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
+
switch(href_list["revolution"])
if("clear")
if(src in ticker.mode.revolutionaries)
@@ -667,6 +677,7 @@ datum/mind
usr << "\red Reequipping revolutionary goes wrong!"
else if (href_list["cult"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["cult"])
if("clear")
if(src in ticker.mode.cult)
@@ -713,6 +724,8 @@ datum/mind
usr << "\red Spawning amulet failed!"
else if (href_list["wizard"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
+
switch(href_list["wizard"])
if("clear")
if(src in ticker.mode.wizards)
@@ -739,6 +752,7 @@ datum/mind
usr << "\blue The objectives for wizard [key] have been generated. You can edit them and anounce manually."
else if (href_list["changeling"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["changeling"])
if("clear")
if(src in ticker.mode.changelings)
@@ -792,6 +806,10 @@ datum/mind
else if (href_list["nuclear"])
+ var/mob/living/carbon/human/H = current
+
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
+
switch(href_list["nuclear"])
if("clear")
if(src in ticker.mode.syndicates)
@@ -818,7 +836,6 @@ datum/mind
if("lair")
current.loc = get_turf(locate("landmark*Syndicate-Spawn"))
if("dressup")
- var/mob/living/carbon/human/H = current
del(H.belt)
del(H.back)
del(H.l_ear)
@@ -846,6 +863,7 @@ datum/mind
usr << "\red No valid nuke found!"
else if (href_list["traitor"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["traitor"])
if("clear")
if(src in ticker.mode.traitors)
@@ -925,6 +943,7 @@ datum/mind
current.radiation -= 50
else if (href_list["silicon"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["silicon"])
if("unmalf")
if(src in ticker.mode.malf_ai)
diff --git a/code/datums/spells/genetic.dm b/code/datums/spells/genetic.dm
index e0d9b2d5b80..c697ea9bc47 100644
--- a/code/datums/spells/genetic.dm
+++ b/code/datums/spells/genetic.dm
@@ -18,7 +18,10 @@
/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets)
for(var/mob/living/target in targets)
- target.mutations.Add(mutations)
+ for(var/x in mutations)
+ target.mutations.Add(x)
+ if(x == M_HULK && ishuman(target))
+ target:hulk_time=world.time + duration
target.disabilities |= disabilities
target.update_mutations() //update target's mutation overlays
spawn(duration)
diff --git a/code/datums/spells/trigger.dm b/code/datums/spells/trigger.dm
index 1b7f2b330ff..631fbcc3010 100644
--- a/code/datums/spells/trigger.dm
+++ b/code/datums/spells/trigger.dm
@@ -12,7 +12,7 @@
var/spell_to_add = text2path(spell)
new spell_to_add(src) //should result in adding to contents, needs testing
-/obj/effect/proc_holder/spell/targeted/trigger/Del()
+/obj/effect/proc_holder/spell/targeted/trigger/Destroy()
for(var/spell in contents)
del(spell)
diff --git a/code/datums/sun.dm b/code/datums/sun.dm
index 9ce8a73cb72..7e1cbb51749 100644
--- a/code/datums/sun.dm
+++ b/code/datums/sun.dm
@@ -6,6 +6,7 @@
var/rate
var/list/solars // for debugging purposes, references solars_list at the constructor
var/nexttime = 3600 // Replacement for var/counter to force the sun to move every X IC minutes
+ var/lastAngleUpdate
/datum/sun/New()
@@ -24,15 +25,25 @@
counter = 0 */
angle = ((rate*world.time/100)%360 + 360)%360
+
/*
Yields a 45 - 75 IC minute rotational period
Rotation rate can vary from 4.8 deg/min to 8 deg/min (288 to 480 deg/hr)
*/
- // To prevent excess server load the server only updates the sun's sight lines every 6 minutes
- if(nexttime < world.time)
+ if(lastAngleUpdate != angle)
+ for(var/obj/machinery/power/tracker/T in solars_list)
+ if(!T.powernet)
+ solars_list.Remove(T)
+ continue
+ T.set_angle(angle)
+ lastAngleUpdate=angle
+
+
+
+ if(nexttime > world.time)
return
- nexttime = nexttime + 3600 // 600 world.time ticks = 1 minute, 3600 = 6 minutes.
+ nexttime = nexttime + 600 // 600 world.time ticks = 1 minute
// now calculate and cache the (dx,dy) increments for line drawing
@@ -54,22 +65,13 @@
dy = c / abs(s)
- for(var/obj/machinery/power/M in solars_list)
+ for(var/obj/machinery/power/solar/S in solars_list)
- if(!M.powernet)
- solars_list.Remove(M)
+ if(!S.powernet)
+ solars_list.Remove(S)
continue
-
- // Solar Tracker
- if(istype(M, /obj/machinery/power/tracker))
- var/obj/machinery/power/tracker/T = M
- T.set_angle(angle)
-
- // Solar Panel
- else if(istype(M, /obj/machinery/power/solar))
- var/obj/machinery/power/solar/S = M
- if(S.control)
- occlusion(S)
+ if(S.control)
+ occlusion(S)
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index d080c3c28ca..11a78e3bf91 100755
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -899,8 +899,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
group = "Hospitality"
/datum/supply_packs/formal_wear
- contains = list(/obj/item/clothing/head/bowler,
- /obj/item/clothing/head/that,
+ contains = list(/obj/item/clothing/head/that,
/obj/item/clothing/suit/storage/lawyer/bluejacket,
/obj/item/clothing/suit/storage/lawyer/purpjacket,
/obj/item/clothing/under/suit_jacket,
diff --git a/code/defines/obj.dm b/code/defines/obj.dm
index 6957aa212bd..d5ad3439f74 100644
--- a/code/defines/obj.dm
+++ b/code/defines/obj.dm
@@ -207,9 +207,16 @@
/*
We can't just insert in HTML into the nanoUI so we need the raw data to play with.
+Instead of creating this list over and over when someone leaves their PDA open to the page
+we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change
+using /obj/effect/datacore/proc/manifest_inject( ), or manifest_insert( )
*/
+var/global/list/PDA_Manifest = list()
+
/obj/effect/datacore/proc/get_manifest_json()
+ if(PDA_Manifest.len)
+ return PDA_Manifest
var/heads[0]
var/sec[0]
var/eng[0]
@@ -269,7 +276,8 @@ We can't just insert in HTML into the nanoUI so we need the raw data to play wit
if(!department && !(name in heads))
misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive)
- return list(\
+
+ PDA_Manifest = list(\
"heads" = heads,\
"sec" = sec,\
"eng" = eng,\
@@ -279,6 +287,7 @@ We can't just insert in HTML into the nanoUI so we need the raw data to play wit
"bot" = bot,\
"misc" = misc\
)
+ return PDA_Manifest
/obj/effect/laser
@@ -312,6 +321,7 @@ We can't just insert in HTML into the nanoUI so we need the raw data to play wit
layer = 2.44 //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4
var/_color = "red"
var/obj/structure/powerswitch/power_switch
+ var/obj/item/device/powersink/attached // holding this here for qdel
/obj/structure/cable/yellow
_color = "yellow"
diff --git a/code/defines/vox_sounds.dm b/code/defines/vox_sounds.dm
index 297790b70b1..7edb13ff0f5 100644
--- a/code/defines/vox_sounds.dm
+++ b/code/defines/vox_sounds.dm
@@ -122,6 +122,7 @@ var/list/vox_sounds = list(
"capture" = 'sound/vox_fem/capture.ogg',
"cargo" = 'sound/vox_fem/cargo.ogg',
"catbeast" = 'sound/vox_fem/catbeast.ogg',
+ "ce" = 'sound/vox_fem/ce.ogg',
"ceiling" = 'sound/vox_fem/ceiling.ogg',
"celsius" = 'sound/vox_fem/celsius.ogg',
"centcomm" = 'sound/vox_fem/centcomm.ogg',
@@ -139,12 +140,15 @@ var/list/vox_sounds = list(
"chef" = 'sound/vox_fem/chef.ogg',
"chefs" = 'sound/vox_fem/chefs.ogg',
"chemical" = 'sound/vox_fem/chemical.ogg',
+ "chemist" = 'sound/vox_fem/chemist.ogg',
+ "chemists" = 'sound/vox_fem/chemists.ogg',
"cleanup" = 'sound/vox_fem/cleanup.ogg',
"clear" = 'sound/vox_fem/clear.ogg',
"clearance" = 'sound/vox_fem/clearance.ogg',
"close" = 'sound/vox_fem/close.ogg',
"clown" = 'sound/vox_fem/clown.ogg',
"clowns" = 'sound/vox_fem/clowns.ogg',
+ "cmo" = 'sound/vox_fem/cmo.ogg',
"code" = 'sound/vox_fem/code.ogg',
"coded" = 'sound/vox_fem/coded.ogg',
"collider" = 'sound/vox_fem/collider.ogg',
@@ -184,6 +188,7 @@ var/list/vox_sounds = list(
"day" = 'sound/vox_fem/day.ogg',
"deactivated" = 'sound/vox_fem/deactivated.ogg',
"dead" = 'sound/vox_fem/dead.ogg',
+ "death" = 'sound/vox_fem/death.ogg',
"decompression" = 'sound/vox_fem/decompression.ogg',
"decontamination" = 'sound/vox_fem/decontamination.ogg',
"deeoo" = 'sound/vox_fem/deeoo.ogg',
@@ -191,8 +196,11 @@ var/list/vox_sounds = list(
"degrees" = 'sound/vox_fem/degrees.ogg',
"delta" = 'sound/vox_fem/delta.ogg',
"denied" = 'sound/vox_fem/denied.ogg',
+ "department" = 'sound/vox_fem/department.ogg',
+ "departments" = 'sound/vox_fem/departments.ogg',
"deploy" = 'sound/vox_fem/deploy.ogg',
"deployed" = 'sound/vox_fem/deployed.ogg',
+ "desk" = 'sound/vox_fem/desk.ogg',
"destroy" = 'sound/vox_fem/destroy.ogg',
"destroyed" = 'sound/vox_fem/destroyed.ogg',
"detain" = 'sound/vox_fem/detain.ogg',
@@ -210,6 +218,7 @@ var/list/vox_sounds = list(
"distortion" = 'sound/vox_fem/distortion.ogg',
"do" = 'sound/vox_fem/do.ogg',
"doctor" = 'sound/vox_fem/doctor.ogg',
+ "doctors" = 'sound/vox_fem/doctors.ogg',
"doop" = 'sound/vox/doop.wav',
"door" = 'sound/vox_fem/door.ogg',
"dorms" = 'sound/vox_fem/dorms.ogg',
@@ -340,6 +349,8 @@ var/list/vox_sounds = list(
"highest" = 'sound/vox_fem/highest.ogg',
"hit" = 'sound/vox_fem/hit.ogg',
"hole" = 'sound/vox_fem/hole.ogg',
+ "hop" = 'sound/vox_fem/hop.ogg',
+ "hos" = 'sound/vox_fem/hos.ogg',
"hostile" = 'sound/vox_fem/hostile.ogg',
"hot" = 'sound/vox_fem/hot.ogg',
"hotel" = 'sound/vox_fem/hotel.ogg',
@@ -539,9 +550,11 @@ var/list/vox_sounds = list(
"raider" = 'sound/vox_fem/raider.ogg',
"raiders" = 'sound/vox_fem/raiders.ogg',
"rapid" = 'sound/vox_fem/rapid.ogg',
+ "rd" = 'sound/vox_fem/rd.ogg',
"reach" = 'sound/vox_fem/reach.ogg',
"reached" = 'sound/vox_fem/reached.ogg',
"reactor" = 'sound/vox_fem/reactor.ogg',
+ "recommend" = 'sound/vox_fem/recommend.ogg',
"red" = 'sound/vox_fem/red.ogg',
"relay" = 'sound/vox_fem/relay.ogg',
"released" = 'sound/vox_fem/released.ogg',
@@ -551,6 +564,7 @@ var/list/vox_sounds = list(
"repair" = 'sound/vox_fem/repair.ogg',
"report" = 'sound/vox_fem/report.ogg',
"reports" = 'sound/vox_fem/reports.ogg',
+ "request" = 'sound/vox_fem/request.ogg',
"requested" = 'sound/vox_fem/requested.ogg',
"required" = 'sound/vox_fem/required.ogg',
"research" = 'sound/vox_fem/research.ogg',
@@ -743,7 +757,6 @@ var/list/vox_sounds = list(
"voltage" = 'sound/vox_fem/voltage.ogg',
"vox" = 'sound/vox_fem/vox.ogg',
"vox_420" = 'sound/vox_fem/vox_420.ogg',
- "vox_login" = 'sound/vox_fem/vox_login.ogg',
"voxtest" = 'sound/vox_fem/voxtest.ogg',
"voxtest2" = 'sound/vox_fem/voxtest2.ogg',
"w" = 'sound/vox_fem/w.ogg',
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 083280790ef..239571b2c6f 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -14,6 +14,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
*/
+
/area
var/fire = null
var/atmos = 1
@@ -32,6 +33,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/eject = null
+ var/debug = 0
+ var/powerupdate = 10 //We give everything 10 ticks to settle out it's power usage.
var/requires_power = 1
var/always_unpowered = 0 //this gets overriden to 1 for space in area/New()
@@ -44,7 +47,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/used_environ = 0
var/has_gravity = 1
-
+ var/list/apc = list()
var/no_air = null
var/area/master // master area used for power calcluations
// (original area before splitting due to sd_DAL)
@@ -352,6 +355,63 @@ proc/process_ghost_teleport_locs()
icon_state = "yellow"
requires_power = 0
+/area/shuttle/salvage
+ name = "\improper Salvage Ship"
+ icon_state = "yellow"
+ requires_power = 0
+
+/area/shuttle/salvage/start
+ name = "\improper Middle of Nowhere"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/arrivals
+ name = "\improper Space Station Auxiliary Docking"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/derelict
+ name = "\improper Derelict Station"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/djstation
+ name = "\improper Ruskie DJ Station"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/north
+ name = "\improper North of the Station"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/east
+ name = "\improper East of the Station"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/south
+ name = "\improper South of the Station"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/commssat
+ name = "\improper The Communications Satellite"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/mining
+ name = "\improper South-West of the Mining Asteroid"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/abandoned_ship
+ name = "\improper Abandoned Ship"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/clown_asteroid
+ name = "\improper Clown Asteroid"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/trading_post
+ name = "\improper Trading Post"
+ icon_state = "yellow"
+
+/area/shuttle/salvage/transit
+ name = "\improper hyperspace"
+ icon_state = "shuttle"
+
/area/airtunnel1/ // referenced in airtunnel.dm:759
/area/dummy/ // Referenced in engine.dm:261
@@ -579,22 +639,18 @@ proc/process_ghost_teleport_locs()
/area/xenos_station/start
name = "\improper start area"
icon_state = "north"
- requires_power = 0
/area/xenos_station/transit
name = "\improper hyperspace"
icon_state = "shuttle"
- requires_power = 0
/area/xenos_station/southwest
name = "\improper aft port solars"
icon_state = "southwest"
- requires_power = 0
/area/xenos_station/northwest
name = "\improper fore port solars"
icon_state = "northwest"
- requires_power = 0
/area/xenos_station/northeast
name = "\improper fore starboard solars"
@@ -604,17 +660,14 @@ proc/process_ghost_teleport_locs()
/area/xenos_station/southeast
name = "\improper aft starboard solars"
icon_state = "southeast"
- requires_power = 0
/area/xenos_station/north
- name = "\improper north landing area"
+ name = "\improper west landing area"
icon_state = "north"
- requires_power = 0
/area/xenos_station/south
- name = "\improper south landing area"
+ name = "\improper east landing area"
icon_state = "south"
- requires_power = 0
//PRISON
/area/prison
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index f13224e1089..8bea8675147 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -13,6 +13,8 @@
master = src //moved outside the spawn(1) to avoid runtimes in lighting.dm when it references loc.loc.master ~Carn
uid = ++global_uid
related = list(src)
+ active_areas += src
+ all_areas += src
if(type == /area) // override defaults for space. TODO: make space areas of type /area/space rather than /area
requires_power = 1
@@ -24,20 +26,16 @@
// lighting_state = 4
//has_gravity = 0 // Space has gravity. Because.. because.
- if(requires_power)
- luminosity = 0
- else
+ if(!requires_power)
power_light = 0 //rastaf0
power_equip = 0 //rastaf0
power_environ = 0 //rastaf0
- luminosity = 1
lighting_use_dynamic = 0
..()
// spawn(15)
power_change() // all machines set to current power level, also updates lighting icon
- InitializeLighting()
/area/proc/poweralert(var/state, var/obj/source as obj)
@@ -83,9 +81,9 @@
reported_danger_level=2
if(reported_danger_level>danger_level)
danger_level=reported_danger_level
- testing("Danger level at [AA.name]: [AA.local_danger_level] (reported [reported_danger_level])")
+// testing("Danger level at [AA.name]: [AA.local_danger_level] (reported [reported_danger_level])")
- testing("Danger level decided upon in [name]: [danger_level] (from [atmosalm])")
+// testing("Danger level decided upon in [name]: [danger_level] (from [atmosalm])")
// Danger level change?
if(danger_level != atmosalm)
@@ -268,6 +266,7 @@
// called when power status changes
/area/proc/power_change()
+ master.powerupdate = 2
for(var/area/RA in related)
for(var/obj/machinery/M in RA) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
@@ -316,54 +315,63 @@
/area/Entered(A)
var/musVolume = 25
var/sound = 'sound/ambience/ambigen1.ogg'
+ var/area/newarea
+ var/area/oldarea
+
+ if(istype(A,/mob))
+ var/mob/M=A
+
+ if(!M.lastarea)
+ M.lastarea = get_area_master(M)
+ newarea = get_area_master(M)
+ oldarea = M.lastarea
+
+ if(newarea==oldarea) return
+
+ M.lastarea = src
+
+ // /vg/ - EVENTS!
+ CallHook("MobAreaChange", list("mob" = M, "new" = newarea, "old" = oldarea))
if(!istype(A,/mob/living)) return
var/mob/living/L = A
if(!L.ckey) return
-
- if(!L.lastarea)
- L.lastarea = get_area(L.loc)
- var/area/newarea = get_area(L.loc)
- var/area/oldarea = L.lastarea
if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together.
thunk(L)
- L.lastarea = newarea
-
// Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch
- if(!(L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE))) return
+ if(L && L.client && (L.client.prefs.toggles & SOUND_AMBIENCE))
+ if(!L.client.ambience_playing)
+ L.client.ambience_playing = 1
+ L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = 2)
- if(!L.client.ambience_playing)
- L.client.ambience_playing = 1
- L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = 2)
+ if(prob(35) && !newarea.media_source)
+ // TODO: This is dumb. - N3X
+ if(istype(src, /area/chapel))
+ sound = pick('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg')
+ else if(istype(src, /area/medical/morgue))
+ sound = pick('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/music/main.ogg')
+ else if(type == /area)
+ sound = pick('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/main.ogg','sound/music/traitor.ogg')
+ else if(istype(src, /area/engine))
+ sound = pick('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
+ else if(istype(src, /area/AIsattele) || istype(src, /area/turret_protected/ai) || istype(src, /area/turret_protected/ai_upload) || istype(src, /area/turret_protected/ai_upload_foyer))
+ sound = pick('sound/ambience/ambimalf.ogg')
+ else if(istype(src, /area/mine/explored) || istype(src, /area/mine/unexplored))
+ sound = pick('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg')
+ musVolume = 25
+ else if(istype(src, /area/tcommsat) || istype(src, /area/turret_protected/tcomwest) || istype(src, /area/turret_protected/tcomeast) || istype(src, /area/turret_protected/tcomfoyer) || istype(src, /area/turret_protected/tcomsat))
+ sound = pick('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
+ else
+ sound = pick('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
- if(prob(35))
-
- if(istype(src, /area/chapel))
- sound = pick('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg')
- else if(istype(src, /area/medical/morgue))
- sound = pick('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/music/main.ogg')
- else if(type == /area)
- sound = pick('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/main.ogg','sound/music/traitor.ogg')
- else if(istype(src, /area/engine))
- sound = pick('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
- else if(istype(src, /area/AIsattele) || istype(src, /area/turret_protected/ai) || istype(src, /area/turret_protected/ai_upload) || istype(src, /area/turret_protected/ai_upload_foyer))
- sound = pick('sound/ambience/ambimalf.ogg')
- else if(istype(src, /area/mine/explored) || istype(src, /area/mine/unexplored))
- sound = pick('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg')
- musVolume = 25
- else if(istype(src, /area/tcommsat) || istype(src, /area/turret_protected/tcomwest) || istype(src, /area/turret_protected/tcomeast) || istype(src, /area/turret_protected/tcomfoyer) || istype(src, /area/turret_protected/tcomsat))
- sound = pick('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
- else
- sound = pick('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
-
- if(!L.client.played)
- L << sound(sound, repeat = 0, wait = 0, volume = musVolume, channel = 1)
- L.client.played = 1
- spawn(600) //ewww - this is very very bad
- if(L.&& L.client)
- L.client.played = 0
+ if(!L.client.played)
+ L << sound(sound, repeat = 0, wait = 0, volume = musVolume, channel = 1)
+ L.client.played = 1
+ spawn(600) //ewww - this is very very bad
+ if(L.&& L.client)
+ L.client.played = 0
/area/proc/gravitychange(var/gravitystate = 0, var/area/A)
diff --git a/code/game/asteroid.dm b/code/game/asteroid.dm
index 05fc66d2d40..a37ac4c2ce4 100644
--- a/code/game/asteroid.dm
+++ b/code/game/asteroid.dm
@@ -161,3 +161,30 @@ proc/make_mining_asteroid_secret(var/size = 5)
return 1
+
+proc/check_complex_placement(var/turf/T,var/size_x,var/size_y,var/ignore_walls=0)
+ var/list/surroundings = list()
+
+ surroundings |= range(7, locate(T.x,T.y,T.z))
+ surroundings |= range(7, locate(T.x+size_x,T.y,T.z))
+ surroundings |= range(7, locate(T.x,T.y+size_y,T.z))
+ surroundings |= range(7, locate(T.x+size_x,T.y+size_y,T.z))
+
+ if(locate(/area/mine/explored) in surroundings) // +5s are for view range
+ return 0
+
+ if(locate(/turf/space) in surroundings)
+ return 0
+
+ /* /vg/: Allow combining rooms.
+ if(locate(/area/asteroid/artifactroom) in surroundings)
+ return 0
+
+ if(locate(/turf/unsimulated/floor/asteroid) in surroundings)
+ return 0
+ */
+
+ // /vg/: Stop spawning shit inside of the vox hideout
+ if(locate(/turf/simulated/wall) in surroundings && !ignore_walls)
+ return 0
+ return 1
\ No newline at end of file
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 234a79c84cf..1ef9a50274c 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -21,6 +21,9 @@
//Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom
+ // Garbage collection
+ var/gc_destroyed=null
+
/atom/proc/throw_impact(atom/hit_atom, var/speed)
if(istype(hit_atom,/mob/living))
var/mob/living/M = hit_atom
@@ -41,6 +44,15 @@
var/mob/living/M = src
M.take_organ_damage(20)
+/atom/Del()
+ // Pass to Destroy().
+ if(!gc_destroyed)
+ Destroy()
+ ..()
+
+/atom/proc/Destroy()
+ gc_destroyed=world.time
+
/atom/proc/CheckParts()
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 72b6e54bf62..8a413b89872 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -32,10 +32,9 @@
src.throw_impact(A)
src.throwing = 0
- spawn( 0 )
- if ((A && yes))
- A.last_bumped = world.time
- A.Bumped(src)
+ if ((A && yes))
+ A.last_bumped = world.time
+ A.Bumped(src)
return
..()
return
@@ -44,7 +43,7 @@
if(destination)
if(loc)
loc.Exited(src)
- loc = destination
+ setloc(destination)
loc.Entered(src)
for(var/atom/movable/AM in loc)
AM.Crossed(src)
@@ -166,8 +165,7 @@
anchored = 1
/atom/movable/overlay/New()
- for(var/x in src.verbs)
- src.verbs -= x
+ verbs.Cut()
return
/atom/movable/overlay/attackby(a, b)
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index 72badcfc103..531fac59e94 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -45,6 +45,9 @@ var/global/list/assigned_blocks[DNA_SE_LENGTH]
var/global/list/datum/dna/gene/dna_genes[0]
+var/global/list/good_blocks[0]
+var/global/list/bad_blocks[0]
+
/////////////////
// GENE DEFINES
/////////////////
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 722439fef01..a179c3dab68 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -24,14 +24,14 @@
/proc/randmutb(var/mob/living/M)
if(!M) return
M.dna.check_integrity()
- var/block = pick(GLASSESBLOCK,COUGHBLOCK,FAKEBLOCK,NERVOUSBLOCK,CLUMSYBLOCK,TWITCHBLOCK,HEADACHEBLOCK,BLINDBLOCK,DEAFBLOCK,HALLUCINATIONBLOCK)
+ var/block = pick(bad_blocks)
M.dna.SetSEState(block, 1)
// Give Random Good Mutation to M
/proc/randmutg(var/mob/living/M)
if(!M) return
M.dna.check_integrity()
- var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,COLDBLOCK,TELEBLOCK,NOBREATHBLOCK,REMOTEVIEWBLOCK,REGENERATEBLOCK,INCREASERUNBLOCK,REMOTETALKBLOCK,MORPHBLOCK,NOPRINTSBLOCK,SHOCKIMMUNITYBLOCK,SMALLSIZEBLOCK)
+ var/block = pick(good_blocks)
M.dna.SetSEState(block, 1)
// Random Appearance Mutation
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index e2224b2c496..5931d0e069b 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -86,14 +86,11 @@
/obj/machinery/dna_scannernew/proc/eject_occupant()
src.go_out()
for(var/obj/O in src)
- if(!istype(O,/obj/item/weapon/circuitboard/clonescanner) && \
- !istype(O,/obj/item/weapon/stock_parts) && \
- !istype(O,/obj/item/stack/cable_coil) && \
- O != beaker)
- O.loc = get_turf(src)//Ejects items that manage to get in there (exluding the components and beaker)
+ if((!istype(O,/obj/item/weapon/reagent_containers)) && (!istype(O,/obj/item/weapon/circuitboard/clonescanner)) && (!istype(O,/obj/item/weapon/stock_parts)) && (!istype(O,/obj/item/stack/cable_coil)))
+ O.setloc(get_turf(src))//Ejects items that manage to get in there (exluding the components)
if(!occupant)
for(var/mob/M in src)//Failsafe so you can get mobs out
- M.loc = get_turf(src)
+ M.setloc(get_turf(src))
/obj/machinery/dna_scannernew/verb/move_inside()
set src in oview(1)
@@ -116,7 +113,7 @@
usr.stop_pulling()
usr.client.perspective = EYE_PERSPECTIVE
usr.client.eye = src
- usr.loc = src
+ usr.setloc(src)
src.occupant = usr
src.icon_state = "scanner_1"
src.add_fingerprint(usr)
@@ -194,7 +191,7 @@
beaker = item
user.drop_item()
- item.loc = src
+ item.setloc(src)
user.visible_message("[user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!")
return
else if (!istype(item, /obj/item/weapon/grab))
@@ -217,7 +214,7 @@
if(M.client)
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
- M.loc = src
+ M.setloc(src)
src.occupant = M
src.icon_state = "scanner_1"
@@ -240,7 +237,7 @@
if (src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
- src.occupant.loc = src.loc
+ src.occupant.setloc(src.loc)
src.occupant = null
src.icon_state = "scanner_0"
return
@@ -249,7 +246,7 @@
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
//Foreach goto(35)
//SN src = null
@@ -258,7 +255,7 @@
if(2.0)
if (prob(50))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
//Foreach goto(108)
//SN src = null
@@ -267,7 +264,7 @@
if(3.0)
if (prob(25))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
//Foreach goto(181)
//SN src = null
@@ -280,7 +277,7 @@
/obj/machinery/dna_scannernew/blob_act()
if(prob(75))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
del(src)
/obj/machinery/computer/scan_consolenew
diff --git a/code/game/dna/genes/goon_disabilities.dm b/code/game/dna/genes/goon_disabilities.dm
index 047edf87483..7a9b3e75186 100644
--- a/code/game/dna/genes/goon_disabilities.dm
+++ b/code/game/dna/genes/goon_disabilities.dm
@@ -66,6 +66,7 @@
// SPEECH MANIPULATORS //
/////////////////////////
+/* Duplicate
// WAS: /datum/bioEffect/stutter
/datum/dna/gene/disability/stutter
name = "Stutter"
@@ -87,6 +88,7 @@
if(is_type_in_list(/datum/dna/gene/disability/speech,M.active_genes))
return 0
return ..(M,flags)
+*/
/* Figure out what the fuck this one does.
// WAS: /datum/bioEffect/smile
@@ -130,6 +132,7 @@
block=CHAVBLOCK
OnSay(var/mob/M, var/message)
+ // THIS ENTIRE THING BEGS FOR REGEX
message = replacetext(message,"dick","prat")
message = replacetext(message,"comdom","knob'ead")
message = replacetext(message,"looking at","gawpin' at")
@@ -144,7 +147,7 @@
message = replacetext(message,"i don't know","wot mate")
message = replacetext(message,"no","naw")
message = replacetext(message,"robust","chin")
- message = replacetext(message,"hi","how what how")
+ message = replacetext(message," hi ","how what how")
message = replacetext(message,"hello","sup bruv")
message = replacetext(message,"kill","bang")
message = replacetext(message,"murder","bang")
@@ -308,16 +311,15 @@
invocation_type = "none"
range = -1
selection_type = "range"
+ var/list/compatible_mobs = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
+ include_user = 1
/obj/effect/proc_holder/spell/targeted/immolate/cast(list/targets)
- if (istype(usr,/mob/living/))
- var/mob/living/L = usr
+ var/mob/living/L = usr
- L.adjust_fire_stacks(0.5) // Same as walking into fire. Was 100 (goon fire)
- L.visible_message("\red [L.name] suddenly bursts into flames!")
- //playsound(L.loc, 'mag_fireballlaunch.ogg', 50, 0)
-
- return
+ L.adjust_fire_stacks(0.5) // Same as walking into fire. Was 100 (goon fire)
+ L.visible_message("\red [L.name] suddenly bursts into flames!")
+ //playsound(L.loc, 'mag_fireballlaunch.ogg', 50, 0)
////////////////////////////////////////////////////////////////////////
diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm
index 50de8fb85de..d5ae6e716a9 100644
--- a/code/game/dna/genes/goon_powers.dm
+++ b/code/game/dna/genes/goon_powers.dm
@@ -1,4 +1,4 @@
-
+#define EAT_MOB_DELAY 300 // 30s
// WAS: /datum/bioEffect/alcres
/datum/dna/gene/basic/sober
@@ -53,7 +53,7 @@
var/turf/simulated/T = get_turf(M)
if(!istype(T))
return
- if(T.lighting_lumcount <= 2)
+ if(T.lit_value <= 2)
M.alpha = 0
else
M.alpha = round(255 * 0.80)
@@ -70,7 +70,7 @@
OnMobLife(var/mob/M)
if((world.time - M.last_movement) >= 30 && !M.stat && M.canmove && !M.restrained())
- M.alpha = round(255 * 0.10)
+ M.alpha = 0
else
M.alpha = round(255 * 0.80)
@@ -122,13 +122,15 @@
panel = "Mutant Powers"
charge_type = "recharge"
- charge_max = 600
+ charge_max = 1200
clothes_req = 0
stat_allowed = 0
invocation_type = "none"
range = 7
selection_type = "range"
+ include_user = 1
+// centcomm_cancast = 0
var/list/compatible_mobs = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
/obj/effect/proc_holder/spell/targeted/cryokinesis/cast(list/targets)
@@ -142,7 +144,7 @@
usr << "\red This will only work on normal organic beings."
return
- C.bodytemperature = -1500
+ C.bodytemperature = -300
C.ExtinguishMob()
C.visible_message("\red A cloud of fine ice crystals engulfs [C]!")
@@ -195,16 +197,19 @@
stat_allowed = 0
invocation_type = "none"
range = 1
- selection_type = "range"
+ selection_type = "view"
+
+ var/list/types_allowed=list(/obj/item,/mob/living/simple_animal, /mob/living/carbon/monkey, /mob/living/carbon/human)
/obj/effect/proc_holder/spell/targeted/eat/choose_targets(mob/user = usr)
var/list/targets = list()
var/list/possible_targets = list()
- for(var/obj/item/O in view_or_range(range, user, selection_type))
- possible_targets += O
+ for(var/atom/movable/O in view_or_range(range, user, selection_type))
+ if(is_type_in_list(O,types_allowed))
+ possible_targets += O
- targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets
+ targets += input("Choose the target of your hunger.", "Targeting") as anything in possible_targets
if(!targets.len) //doesn't waste the spell
revert_cast(user)
@@ -212,20 +217,9 @@
perform(targets)
-/obj/effect/proc_holder/spell/targeted/eat/cast(list/targets)
- if(!targets.len)
- usr << "No target found in range."
- return
-
- var/obj/item/the_item = targets[1]
-
- usr.visible_message("\red [usr] eats [the_item].")
- playsound(usr.loc, 'sound/items/eatfood.ogg', 50, 0)
-
- del(the_item)
-
- if(ishuman(usr))
- var/mob/living/carbon/human/H=usr
+/obj/effect/proc_holder/spell/targeted/eat/proc/doHeal(var/mob/user)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H=user
for(var/name in H.organs_by_name)
var/datum/organ/external/affecting = null
if(!H.organs[name])
@@ -234,8 +228,63 @@
if(!istype(affecting, /datum/organ/external))
continue
affecting.heal_damage(4, 0)
- usr:UpdateDamageIcon()
- usr:updatehealth()
+ H.UpdateDamageIcon()
+ H.updatehealth()
+
+/obj/effect/proc_holder/spell/targeted/eat/cast(list/targets)
+ if(!targets.len)
+ usr << "No target found in range."
+ return
+
+ var/atom/movable/the_item = targets[1]
+ if(ishuman(the_item))
+ //My gender
+ var/m_his="his"
+ if(usr.gender==FEMALE)
+ m_his="her"
+ // Their gender
+ var/t_his="his"
+ if(the_item.gender==FEMALE)
+ t_his="her"
+ var/mob/living/carbon/human/H = the_item
+ var/datum/organ/external/limb = H.get_organ(usr.zone_sel.selecting)
+ if(!istype(limb))
+ usr << "\red You can't eat this part of them!"
+ revert_cast()
+ return 0
+ if(istype(limb,/datum/organ/external/head))
+ // Bullshit, but prevents being unable to clone someone.
+ usr << "\red You try to put \the [limb] in your mouth, but [t_his] ears tickle your throat!"
+ revert_cast()
+ return 0
+ if(istype(limb,/datum/organ/external/chest))
+ // Bullshit, but prevents being able to instagib someone.
+ usr << "\red You try to put their [limb] in your mouth, but it's too big to fit!"
+ revert_cast()
+ return 0
+ usr.visible_message("\red [usr] begins stuffing [the_item]'s [limb.display_name] into [m_his] gaping maw!")
+ var/oldloc = H.loc
+ if(!do_mob(usr,H,EAT_MOB_DELAY))
+ usr << "\red You were interrupted before you could eat [the_item]!"
+ else
+ if(!limb || !H)
+ return
+ if(H.loc!=oldloc)
+ usr << "\red \The [limb] moved away from your mouth!"
+ return
+ usr.visible_message("\red [usr] [pick("chomps","bites")] off [the_item]'s [limb]!")
+ playsound(usr.loc, 'sound/items/eatfood.ogg', 50, 0)
+ var/obj/limb_obj=limb.droplimb(1,1)
+ if(limb_obj)
+ var/datum/organ/external/chest=usr:get_organ("chest")
+ chest.implants += limb_obj
+ limb_obj.loc=usr
+ doHeal(usr)
+ else
+ usr.visible_message("\red [usr] eats \the [the_item].")
+ playsound(usr.loc, 'sound/items/eatfood.ogg', 50, 0)
+ del(the_item)
+ doHeal(usr)
return
diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm
index ebb15a714ad..3562639c84b 100644
--- a/code/game/dna/genes/monkey.dm
+++ b/code/game/dna/genes/monkey.dm
@@ -9,7 +9,7 @@
/datum/dna/gene/monkey/activate(var/mob/living/M, var/connected, var/flags)
if(!istype(M,/mob/living/carbon/human))
- testing("Cannot monkey-ify [M], type is [M.type].")
+// testing("Cannot monkey-ify [M], type is [M.type].")
return
var/mob/living/carbon/human/H = M
H.monkeyizing = 1
diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm
index 9f7bdcc4569..57e0680b933 100644
--- a/code/game/dna/genes/powers.dm
+++ b/code/game/dna/genes/powers.dm
@@ -138,6 +138,7 @@
..(M,connected,flags)
M.pass_flags |= 1
+/* OLD HULK BEHAVIOR
/datum/dna/gene/basic/hulk
name="Hulk"
activation_messages=list("Your muscles hurt.")
@@ -147,27 +148,29 @@
block=HULKBLOCK
can_activate(var/mob/M,var/flags)
- // Can't be big and small.
+ // Can't be big AND small.
if(M_DWARF in M.mutations)
return 0
return ..(M,flags)
OnDrawUnderlays(var/mob/M,var/g,var/fat)
- if(fat)
- return "hulk_[fat]_s"
- else
- return "hulk_[g]_s"
+ if(M_HULK in M.mutations)
+ if(fat)
+ return "hulk_[fat]_s"
+ else
+ return "hulk_[g]_s"
return 0
OnMobLife(var/mob/living/carbon/human/M)
if(!istype(M)) return
if(M.health <= 25)
M.mutations.Remove(M_HULK)
+ M.dna.SetSEState(HULKBLOCK,0)
M.update_mutations() //update our mutation overlays
M << "\red You suddenly feel very weak."
M.Weaken(3)
M.emote("collapse")
-
+*/
/datum/dna/gene/basic/xray
name="X-Ray Vision"
activation_messages=list("The walls suddenly disappear.")
diff --git a/code/game/dna/genes/vg_powers.dm b/code/game/dna/genes/vg_powers.dm
new file mode 100644
index 00000000000..bc85838b07f
--- /dev/null
+++ b/code/game/dna/genes/vg_powers.dm
@@ -0,0 +1,83 @@
+/*
+This is /vg/'s nerf for hulk. Feel free to steal it.
+
+Obviously, requires DNA2.
+*/
+
+// When hulk was first applied (world.time).
+/mob/living/carbon/human/var/hulk_time=0
+
+// In decaseconds.
+#define HULK_DURATION 300
+#define HULK_COOLDOWN 600
+
+/datum/dna/gene/basic/grant_spell/hulk
+ name = "Hulk"
+ desc = "Allows the subject to become the motherfucking Hulk."
+ activation_messages = list("Your muscles hurt.")
+ deactivation_messages = list("Your muscles quit tensing.")
+
+ spelltype = /obj/effect/proc_holder/spell/targeted/hulk
+
+ New()
+ ..()
+ block = HULKBLOCK
+
+ can_activate(var/mob/M,var/flags)
+ // Can't be big AND small.
+ if(M_DWARF in M.mutations)
+ return 0
+ return ..(M,flags)
+
+ OnDrawUnderlays(var/mob/M,var/g,var/fat)
+ if(M_HULK in M.mutations)
+ if(fat)
+ return "hulk_[fat]_s"
+ else
+ return "hulk_[g]_s"
+ return 0
+
+ OnMobLife(var/mob/living/carbon/human/M)
+ if(!istype(M)) return
+ if(M_HULK in M.mutations)
+ var/timeleft=M.hulk_time - world.time
+ if(M.health <= 25 || timeleft <= 0)
+ M.hulk_time=0 // Just to be sure.
+ M.mutations.Remove(M_HULK)
+ //M.dna.SetSEState(HULKBLOCK,0)
+ M.update_mutations() //update our mutation overlays
+ M.update_body()
+ M << "\red You suddenly feel very weak."
+ M.Weaken(3)
+ M.emote("collapse")
+
+/obj/effect/proc_holder/spell/targeted/hulk
+ name = "Hulk Out"
+ panel = "Mutant Powers"
+ range = -1
+ include_user = 1
+
+ charge_type = "recharge"
+ charge_max = HULK_COOLDOWN
+
+ clothes_req = 0
+ stat_allowed = 0
+
+ invocation_type = "none"
+
+/obj/effect/proc_holder/spell/targeted/hulk/New()
+ desc = "Get mad! For [HULK_DURATION/10] seconds, anyway."
+ ..()
+
+/obj/effect/proc_holder/spell/targeted/hulk/cast(list/targets)
+ if (istype(usr.loc,/mob/))
+ usr << "\red You can't hulk out right now!"
+ return
+ var/mob/living/carbon/human/M=usr
+ M.hulk_time = world.time + HULK_DURATION
+ M.mutations.Add(M_HULK)
+ M.update_mutations() //update our mutation overlays
+ M.update_body()
+ //M.say(pick("",";")+pick("HULK MAD","YOU MADE HULK ANGRY")) // Just a note to security.
+ message_admins("[key_name(usr)] has hulked out! ([formatJumpTo(usr)])")
+ return
\ No newline at end of file
diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm
index aeb313eed02..70105d2264d 100644
--- a/code/game/gamemodes/autotraitor/autotraitor.dm
+++ b/code/game/gamemodes/autotraitor/autotraitor.dm
@@ -133,6 +133,7 @@
newtraitor << "\red ATTENTION: \black It is time to pay your debt to the Syndicate..."
newtraitor << "You are now a traitor."
newtraitor.mind.special_role = "traitor"
+ newtraitor.hud_updateflag |= 1 << SPECIALROLE_HUD
var/obj_count = 1
newtraitor << "\blue Your current objectives:"
for(var/datum/objective/objective in newtraitor.mind.objectives)
diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm
index 6b6db7d0768..5abacbeae6a 100644
--- a/code/game/gamemodes/blob/blob.dm
+++ b/code/game/gamemodes/blob/blob.dm
@@ -175,10 +175,10 @@ var/list/blob_nodes = list()
return
if (1)
- command_alert("Confirmed outbreak of level 7 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert")
+ command_alert("NanoTrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.", "Biohazard Alert")
for(var/mob/M in player_list)
if(!istype(M,/mob/new_player))
- M << sound('sound/AI/outbreak7.ogg')
+ M << sound('sound/AI/blob_confirmed.ogg')
return
if (2)
diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm
index 36c6f458544..4f32d870afe 100644
--- a/code/game/gamemodes/blob/blob_report.dm
+++ b/code/game/gamemodes/blob/blob_report.dm
@@ -32,7 +32,7 @@
intercepttext += "Message ends."
for (var/mob/living/silicon/ai/aiPlayer in player_list)
if (aiPlayer.client)
- var/law = "The station is under quarantine, prevent biological entities from leaving the station at all costs while minimizing collateral damage. The nuclear failsafe must be activated at any cost, the code is: [get_nuke_code()]."
+ var/law = "The station is under quarantine, prevent biological entities from leaving the station at all costs. The nuclear failsafe must be activated at any cost, the code is: [get_nuke_code()]."
aiPlayer.set_zeroth_law(law)
aiPlayer << "\red You have detected a change in your laws information:"
aiPlayer << "Laws Updated: [law]"
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index e928c47f1d8..9aa2fb50d79 100644
--- a/code/game/gamemodes/blob/blobs/core.dm
+++ b/code/game/gamemodes/blob/blobs/core.dm
@@ -20,7 +20,7 @@
..(loc, h)
- Del()
+ Destroy()
blob_cores -= src
if(overmind)
del(overmind)
@@ -28,9 +28,6 @@
..()
return
- fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- return
-
update_icon()
if(health <= 0)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm
index 0322a33cee8..35130546fcd 100644
--- a/code/game/gamemodes/blob/blobs/factory.dm
+++ b/code/game/gamemodes/blob/blobs/factory.dm
@@ -118,7 +118,7 @@
del(src)
-/mob/living/simple_animal/hostile/blobspore/Del()
+/mob/living/simple_animal/hostile/blobspore/Destroy()
if(factory)
factory.spores -= src
if(contents)
diff --git a/code/game/gamemodes/blob/blobs/node.dm b/code/game/gamemodes/blob/blobs/node.dm
index 0ce8fb208a8..8934582a6d4 100644
--- a/code/game/gamemodes/blob/blobs/node.dm
+++ b/code/game/gamemodes/blob/blobs/node.dm
@@ -11,10 +11,7 @@
processing_objects.Add(src)
..(loc, h)
- fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- return
-
- Del()
+ Destroy()
blob_nodes -= src
processing_objects.Remove(src)
..()
diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm
index f58f7b5af68..c0b1d2aa550 100644
--- a/code/game/gamemodes/blob/blobs/shield.dm
+++ b/code/game/gamemodes/blob/blobs/shield.dm
@@ -4,7 +4,7 @@
icon_state = "blob_idle"
desc = "Some blob creature thingy"
health = 60
- brute_resist = 1
+ brute_resist = 4
fire_resist = 2
@@ -15,9 +15,6 @@
return
return
- fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- return
-
CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(istype(mover) && mover.checkpass(PASSBLOB)) return 1
return 0
diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm
index 995cfed91fb..da5ec83eef6 100644
--- a/code/game/gamemodes/blob/overmind.dm
+++ b/code/game/gamemodes/blob/overmind.dm
@@ -26,21 +26,61 @@
sync_mind()
src << "You are the overmind!"
- src << "You are the overmind and can control the blob by placing new blob pieces such as..."
+ src << "You are the overmind and can control the blob! You can expand, which will attack people, and place new blob pieces such as..."
src << "Normal Blob will expand your reach and allow you to upgrade into special blobs that perform certain functions."
src << "Shield Blob is a strong and expensive blob which can take more damage. It is fireproof and can block air, use this to protect yourself from station fires."
src << "Resource Blob is a blob which will collect more resources for you, try to build these earlier to get a strong income. It will benefit from being near your core or multiple nodes, by having an increased resource rate; put it alone and it won't create resources at all."
src << "Node Blob is a blob which will grow, like the core. Unlike the core it won't give you a small income but it can power resource and factory blobs to increase their rate."
src << "Factory Blob is a blob which will spawn blob spores which will attack nearby food. Putting this nearby nodes and your core will increase the spawn rate; put it alone and it will not spawn any spores."
+ src << "Shortcuts: CTRL Click = Expand Blob / Middle Mouse Click = Rally Spores / Alt Click = Create Shield"
+ update_health()
+/mob/camera/blob/proc/update_health()
+ if(blob_core)
+ hud_used.blobhealthdisplay.maptext = " [blob_core.health]
"
-mob/camera/blob/Life()
- hud_used.blobpwrdisplay.maptext = " [src.blob_points]
"
- hud_used.blobhealthdisplay.maptext = " [blob_core.health]
"
- return
+/mob/camera/blob/proc/add_points(var/points)
+ if(points != 0)
+ blob_points = Clamp(blob_points + points, 0, max_blob_points)
+ //sanity for manual spawned blob cameras
+ if(hud_used)
+ hud_used.blobpwrdisplay.maptext = " [src.blob_points]
"
/mob/camera/blob/say(var/message)
- return//No talking for you
+ if (!message)
+ return
+
+ if (src.client)
+ if(client.prefs.muted & MUTE_IC)
+ src << "You cannot send IC messages (muted)."
+ return
+ if (src.client.handle_spam_prevention(message,MUTE_IC))
+ return
+
+ if (stat)
+ return
+
+ blob_talk(message)
+
+/mob/camera/blob/proc/blob_talk(message)
+ log_say("[key_name(src)] : [message]")
+
+ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+
+ if (!message)
+ return
+
+ var/message_a = say_quote(message)
+ var/rendered = "Blob Telepathy, [name] [message_a]"
+
+ for (var/mob/camera/blob/S in world)
+ if(istype(S))
+ S.show_message(rendered, 2)
+
+ for (var/mob/M in dead_mob_list)
+ if(!istype(M,/mob/new_player) && !istype(M,/mob/living/carbon/brain)) //No meta-evesdropping
+ rendered = "Blob Telepathy, [name] (Follow) [message_a]"
+ M.show_message(rendered, 2)
/mob/camera/blob/emote(var/act,var/m_type=1,var/message = null)
return
@@ -64,3 +104,5 @@ mob/camera/blob/Life()
loc = NewLoc
else
return 0
+
+
diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm
index 370fd56f7c1..731b32aeb14 100644
--- a/code/game/gamemodes/blob/powers.dm
+++ b/code/game/gamemodes/blob/powers.dm
@@ -4,13 +4,9 @@
if(blob_points < cost)
src << "You cannot afford this."
return 0
- blob_points -= cost
+ add_points(-cost)
return 1
-/mob/camera/blob/proc/add_points(var/points = 0)
- if(points)
- blob_points = min(max_blob_points, blob_points + points)
-
// Power verbs
/mob/camera/blob/verb/transport_core()
@@ -35,16 +31,15 @@
if(chosen_node)
src.loc = chosen_node.loc
-/mob/camera/blob/verb/create_shield()
+/mob/camera/blob/verb/create_shield_power()
set category = "Blob"
set name = "Create Shield Blob (10)"
set desc = "Create a shield blob."
-
var/turf/T = get_turf(src)
+ create_shield(T)
- if(!T)
- return
+/mob/camera/blob/proc/create_shield(var/turf/T)
var/obj/effect/blob/B = (locate(/obj/effect/blob) in T)
@@ -64,6 +59,7 @@
return
+
/mob/camera/blob/verb/create_resource()
set category = "Blob"
set name = "Create Resource Blob (40)"
@@ -85,7 +81,7 @@
src << "Unable to use this blob, find a normal one."
return
- for(var/obj/effect/blob/resource/blob in orange(4))
+ for(var/obj/effect/blob/resource/blob in orange(4, T))
src << "There is a resource blob nearby, move more than 4 tiles away from it!"
return
@@ -154,7 +150,7 @@
src << "Unable to use this blob, find a normal one."
return
- for(var/obj/effect/blob/node/blob in orange(5))
+ for(var/obj/effect/blob/node/blob in orange(5, T))
src << "There is another node nearby, move more than 5 tiles away from it!"
return
@@ -186,7 +182,7 @@
src << "Unable to use this blob, find a normal one."
return
- for(var/obj/effect/blob/factory/blob in orange(7))
+ for(var/obj/effect/blob/factory/blob in orange(7, T))
src << "There is a factory blob nearby, move more than 7 tiles away from it!"
return
@@ -219,13 +215,15 @@
return
-/mob/camera/blob/verb/spawn_blob()
+/mob/camera/blob/verb/expand_blob_power()
set category = "Blob"
- set name = "Expand Blob (5)"
+ set name = "Expand/Attack Blob (5)"
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear we will attack it, which might clear it."
var/turf/T = get_turf(src)
+ expand_blob(T)
+/mob/camera/blob/proc/expand_blob(var/turf/T)
if(!T)
return
@@ -234,7 +232,7 @@
src << "There is a blob here!"
return
- var/obj/effect/blob/OB = locate() in circlerange(src, 1)
+ var/obj/effect/blob/OB = locate() in circlerange(T, 1)
if(!OB)
src << "There is no blob adjacent to you."
return
@@ -249,56 +247,28 @@
OB.expand(T, 0)
return
-/mob/camera/blob/proc/click_create_shield(obj/effect/blob/B)
- if(!B)//We are on a blob
- src << "There is no blob here!"
- return
- if(!istype(B, /obj/effect/blob/normal))
- src << "Unable to use this blob, find a normal one."
- return
-
- if(!can_buy(10))
- return
-
-
- B.change_to(/obj/effect/blob/shield)
- return
-
-/mob/camera/blob/proc/click_expand_blob(var/turf/T)
-
- if(!T)
- return
-
- var/obj/effect/blob/B = locate() in T
- if(B)
- src << "There is a blob here!"
- return
-
- var/obj/effect/blob/OB = locate() in circlerange(T, 1)
- if(!OB)
- src << "There is no blob adjacent to that tile."
- return
-
- if(!can_buy(5))
- return
- OB.expand(T, 0)
- return
-
-/mob/camera/blob/verb/rally_spores()
+/mob/camera/blob/verb/rally_spores_power()
set category = "Blob"
set name = "Rally Spores (5)"
set desc = "Rally the spores to move to your location."
+ var/turf/T = get_turf(src)
+ rally_spores(T)
+
+/mob/camera/blob/proc/rally_spores(var/turf/T)
+
if(!can_buy(5))
return
- var/list/surrounding_turfs = block(locate(x - 1, y - 1, z), locate(x + 1, y + 1, z))
+ src << "You rally your spores."
+
+ var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
if(!surrounding_turfs.len)
return
for(var/mob/living/simple_animal/hostile/blobspore/BS in living_mob_list)
- if(isturf(BS.loc) && get_dist(BS, src) <= 20)
+ if(isturf(BS.loc) && get_dist(BS, T) <= 35)
BS.LoseTarget()
BS.Goto(pick(surrounding_turfs), BS.move_to_delay)
return
\ No newline at end of file
diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm
index 5bbf49b843d..9fdcc7ce7ba 100644
--- a/code/game/gamemodes/blob/theblob.dm
+++ b/code/game/gamemodes/blob/theblob.dm
@@ -2,12 +2,13 @@
/obj/effect/blob
name = "blob"
icon = 'icons/mob/blob.dmi'
- luminosity = 3
+ luminosity = 0
desc = "Some blob creature thingy"
density = 0
opacity = 0
anchored = 1
var/health = 30
+ var/health_timestamp = 0
var/brute_resist = 4
var/fire_resist = 1
@@ -22,7 +23,7 @@
return
- Del()
+ Destroy()
blobs -= src
..()
return
@@ -40,12 +41,14 @@
fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
..()
- var/damage = Clamp(0.01 * exposed_temperature / fire_resist, 0, 4 - fire_resist)
+ var/damage = Clamp(0.03 * exposed_temperature / fire_resist, 0, 7 - fire_resist)
if(damage)
health -= damage
update_icon()
proc/Life()
+ for(var/atom/A in src.loc)//Hit everything in the turf
+ A.blob_act()
return
@@ -56,7 +59,7 @@
if(run_action())//If we can do something here then we dont need to pulse more
return
- if(pulse > 30)
+ if(pulse > 20)
return//Inf loop check
//Looking for another blob to pulse
diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm
index 202dbf7ab76..539ef55529b 100644
--- a/code/game/gamemodes/changeling/changeling_powers.dm
+++ b/code/game/gamemodes/changeling/changeling_powers.dm
@@ -377,27 +377,78 @@
spawn(rand(800,2000))
if(changeling_power(20,1,100,DEAD))
+ // charge the changeling chemical cost for stasis
changeling.chem_charges -= 20
- if(C.stat == DEAD)
- dead_mob_list -= C
- living_mob_list += C
- C.stat = CONSCIOUS
- C.tod = null
+
+ // shut down various types of badness
C.setToxLoss(0)
C.setOxyLoss(0)
C.setCloneLoss(0)
+ C.setBrainLoss(0)
C.SetParalysis(0)
C.SetStunned(0)
C.SetWeakened(0)
+
+ // shut down ongoing problems
C.radiation = 0
- C.heal_overall_damage(C.getBruteLoss(), C.getFireLoss())
+ C.nutrition = 400
+ C.bodytemperature = 310
+ C.sdisabilities = 0
+ C.disabilities = 0
+ C.blinded = 0
+
+ // fix blindness and deafness
+ C.eye_blind = 0
+ C.eye_blurry = 0
+ C.ear_deaf = 0
+ C.ear_damage = 0
+
+ // head actual damage numbers
+ C.heal_overall_damage(1000, 1000)
+
+ // get rid of the reagents
C.reagents.clear_reagents()
+
+ // cure diseases
+ for(var/datum/disease/D in viruses)
+ D.cure(0)
+
+ // fix all the organs
+ C.restore_all_organs()
+
+ // restore blood
+ if(ishuman(C))
+ var/mob/living/carbon/human/human_mob = C
+ human_mob.restore_blood()
+
+ // remove the character from the list of the dead
+ if(C.stat == 2)
+ dead_mob_list -= src
+ living_mob_list += src
+
+ // make us conscious again
+ C.stat = CONSCIOUS
+
+ // remove the time of death
+ C.tod = null
+
+ // fix all the icons
+ C.regenerate_icons()
+
+ // remove our fake death flag
+ C.status_flags &= ~(FAKEDEATH)
+
+ // let us move again
+ C.update_canmove()
+
+ // re-add out changeling powers
+ C.make_changeling()
+
+ // sending display messages
C << "We have regenerated."
C.visible_message("[src] appears to wake from the dead, having healed all wounds.")
-
- C.status_flags &= ~(FAKEDEATH)
- C.update_canmove()
- C.make_changeling()
+
+
feedback_add_details("changeling_powers","FD")
return 1
@@ -762,7 +813,7 @@ var/list/datum/dna/hivemind_bank = list()
T << "You feel a small prick and your chest becomes tight."
T.silent = 10
T.Paralyse(10)
- T.make_jittery(1000)
+ T.Jitter(1000)
if(T.reagents) T.reagents.add_reagent("lexorin", 40)
feedback_add_details("changeling_powers","DTHS")
return 1
@@ -785,4 +836,4 @@ var/list/datum/dna/hivemind_bank = list()
changeling.absorbed_dna |= T.dna
feedback_add_details("changeling_powers","ED")
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 82a1c6d17d4..c49bcdbdddc 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -26,7 +26,7 @@
/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
if(!iscultist(user))
user << "\red An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly."
- user.make_dizzy(120)
+ user.Dizzy(120)
/obj/item/clothing/head/culthood
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index f8c6c3e1810..e08df1a893e 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -775,13 +775,9 @@ var/list/sacrificed = list()
return
cultist.buckled = null
if (cultist.handcuffed)
- cultist.handcuffed.loc = cultist.loc
- cultist.handcuffed = null
- cultist.update_inv_handcuffed()
+ cultist.drop_from_inventory(cultist.handcuffed)
if (cultist.legcuffed)
- cultist.legcuffed.loc = cultist.loc
- cultist.legcuffed = null
- cultist.update_inv_legcuffed()
+ cultist.drop_from_inventory(cultist.legcuffed)
if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle))
cultist.u_equip(cultist.wear_mask)
if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded)
diff --git a/code/game/gamemodes/events/biomass.dm b/code/game/gamemodes/events/biomass.dm
index 4835d9a61b6..61d833d6846 100644
--- a/code/game/gamemodes/events/biomass.dm
+++ b/code/game/gamemodes/events/biomass.dm
@@ -14,7 +14,7 @@
New()
return
- Del()
+ Destroy()
if(master)
master.vines -= src
master.growth_queue -= src
@@ -60,7 +60,7 @@
spawn_biomass_piece(src.loc)
processing_objects.Add(src)
- Del()
+ Destroy()
processing_objects.Remove(src)
..()
diff --git a/code/game/gamemodes/events/ninja_equipment.dm b/code/game/gamemodes/events/ninja_equipment.dm
index 1ba50d0a1e1..0d94cab437a 100644
--- a/code/game/gamemodes/events/ninja_equipment.dm
+++ b/code/game/gamemodes/events/ninja_equipment.dm
@@ -40,7 +40,7 @@ ________________________________________________________________________________
cell = new/obj/item/weapon/cell/high//The suit should *always* have a battery because so many things rely on it.
cell.charge = 9000//Starting charge should not be higher than maximum charge. It leads to problems with recharging.
-/obj/item/clothing/suit/space/space_ninja/Del()
+/obj/item/clothing/suit/space/space_ninja/Destroy()
if(affecting)//To make sure the window is closed.
affecting << browse(null, "window=hack spideros")
if(AI)//If there are AIs present when the ninja kicks the bucket.
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index 31bb844e55f..6b33c2e5ab5 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -39,6 +39,8 @@ var/global/datum/controller/gameticker/ticker
/datum/controller/gameticker/proc/pregame()
login_music = pick(\
+ 'sound/music/therock.ogg',\
+ 'sound/music/klendathu.ogg',\
'sound/music/knights.ogg',\
'sound/music/space.ogg',\
'sound/music/Title1.ogg',\
@@ -136,6 +138,15 @@ var/global/datum/controller/gameticker/ticker
//Deleting Startpoints but we need the ai point to AI-ize people later
if (S.name != "AI")
del(S)
+ var/list/obj/effect/landmark/spacepod/random/L = list()
+ for(var/obj/effect/landmark/spacepod/random/SS in landmarks_list)
+ if(istype(SS))
+ L += SS
+ var/obj/effect/landmark/spacepod/random/S = pick(L)
+ new /obj/spacepod/random(S.loc)
+ for(var/obj in L)
+ if(istype(obj, /obj/effect/landmark/spacepod/random))
+ del(obj)
world << "Enjoy the game!"
world << sound('sound/AI/welcome.ogg') // Skie
//Holiday Round-start stuff ~Carn
@@ -153,7 +164,7 @@ var/global/datum/controller/gameticker/ticker
supply_shuttle.process() //Start the supply shuttle regenerating points -- TLE
master_controller.process() //Start master_controller.process()
- lighting_controller.process() //Start processing DynamicAreaLighting updates
+ //lighting_controller.process() //Start processing DynamicAreaLighting updates
if(config.sql_enabled)
diff --git a/code/game/gamemodes/intercept_report.dm b/code/game/gamemodes/intercept_report.dm
index 54c1ac6b079..2ee0fe7d919 100644
--- a/code/game/gamemodes/intercept_report.dm
+++ b/code/game/gamemodes/intercept_report.dm
@@ -213,11 +213,11 @@
if(prob(prob_right_job))
if(correct_person)
if(correct_person:assigned_role=="MODE")
- changeling_job = pick(get_all_jobs())
+ changeling_job = pick(joblist)
else
changeling_job = correct_person:assigned_role
else
- changeling_job = pick(get_all_jobs())
+ changeling_job = pick(joblist)
if(prob(prob_right_dude) && ticker.mode == "changeling")
if(correct_person:assigned_role=="MODE")
changeling_name = correct_person:current
diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm
index 69eda2a405b..c658e046c87 100644
--- a/code/game/gamemodes/malfunction/malfunction.dm
+++ b/code/game/gamemodes/malfunction/malfunction.dm
@@ -30,7 +30,7 @@
/datum/game_mode/malfunction/pre_setup()
for(var/mob/new_player/player in player_list)
- if(player.mind && player.mind.assigned_role == "AI")
+ if(player.mind && player.mind.assigned_role == "AI" && (player.client.prefs.be_special & BE_MALF))
malf_ai+=player.mind
if(malf_ai.len)
return 1
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index b8faeeef976..a743ad79a08 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -417,7 +417,7 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob)
return
return
-/obj/item/weapon/disk/nuclear/Del()
+/obj/item/weapon/disk/nuclear/Destroy()
if(blobstart.len > 0)
var/obj/D = new /obj/item/weapon/disk/nuclear(pick(blobstart))
message_admins("[src] has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).")
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 1be0c2fe405..6409063e5f7 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -1,10 +1,12 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
+var/global/list/all_objectives = list()
var/list/potential_theft_objectives=typesof(/datum/theft_objective) \
- /datum/theft_objective \
- /datum/theft_objective/special \
- /datum/theft_objective/number \
- - /datum/theft_objective/number/special
+ - /datum/theft_objective/number/special \
+ - /datum/theft_objective/number/coins
datum/objective
var/datum/mind/owner = null //Who owns the objective.
@@ -14,9 +16,14 @@ datum/objective
var/completed = 0 //currently only used for custom objectives.
New(var/text)
+ all_objectives |= src
if(text)
explanation_text = text
+ Del()
+ all_objectives -= src
+ ..()
+
proc/check_completion()
return completed
@@ -260,7 +267,6 @@ datum/objective/protect//The opposite of killing a dude.
explanation_text = "Free Objective"
return target
-
find_target_by_role(role, role_type=0)
..(role, role_type)
if(target && target.current)
@@ -299,6 +305,36 @@ datum/objective/hijack
return 0
return 1
+datum/objective/speciesist
+ var/datum/species/speciesist_target = null
+
+ find_target()
+ var/list/possible_targets = list()
+ var/mob/living/carbon/human/O = owner.current
+ for(var/datum/mind/possible_target in ticker.minds)
+ if(ishuman(possible_target.current))
+ var/mob/living/carbon/human/M = possible_target.current
+ if (M.species != O.species)
+ possible_targets += M.species
+ if(possible_targets.len > 0)
+ speciesist_target = pick(possible_targets)
+ explanation_text = "Do not allow any [speciesist_target.name]s to escape on the shuttle."
+ else
+ explanation_text = "Free Objective"
+ return speciesist_target
+
+ check_completion()
+ if(emergency_shuttle.location<2)
+ return 0
+ var/area/shuttle = locate(/area/shuttle/escape/centcom)
+ for(var/mob/living/carbon/human/player in player_list)
+ if (player.mind)
+ if (player.species == speciesist_target)
+ if(player.stat != DEAD) //they're not dead!
+ if(get_turf(player) in shuttle)
+ return 0
+ return 1
+
datum/objective/block
explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive."
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index b4660241c46..12e851d064e 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -202,6 +202,7 @@
if(rev_mind in revolutionaries)
revolutionaries -= rev_mind
rev_mind.special_role = null
+ rev_mind.current.hud_updateflag |= 1 << SPECIALROLE_HUD
if(beingborged)
rev_mind.current << "\red The frame's firmware detects and deletes your neural reprogramming! You remember nothing from the moment you were flashed until now."
diff --git a/code/game/gamemodes/revolution/rp-revolution.dm b/code/game/gamemodes/revolution/rp-revolution.dm
index 7456e73403b..963579af7c8 100644
--- a/code/game/gamemodes/revolution/rp-revolution.dm
+++ b/code/game/gamemodes/revolution/rp-revolution.dm
@@ -304,4 +304,4 @@ mob/living/carbon/human/proc
else if(choice == "No!")
M << "\red You reject this traitorous cause!"
src << "\red [M] does not support the revolution!"
- M.mind.rev_cooldown = world.time+50
\ No newline at end of file
+ M.mind.rev_cooldown = world.time+50
diff --git a/code/game/gamemodes/revolution/rp_revolution.dm b/code/game/gamemodes/revolution/rp_revolution.dm
index 135ffa956a7..522cc2cf701 100644
--- a/code/game/gamemodes/revolution/rp_revolution.dm
+++ b/code/game/gamemodes/revolution/rp_revolution.dm
@@ -110,6 +110,7 @@
rev_mind.current << "\red You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill, capture or convert the heads to win the revolution!"
rev_mind.special_role = "Revolutionary"
update_rev_icons_added(rev_mind)
+ H.hud_updateflag |= 1 << SPECIALROLE_HUD
return 1
/////////////////////////////
@@ -155,9 +156,17 @@
return 1
-/mob/living/carbon/human/proc/RevConvert(mob/M as mob in oview(src))
+/mob/living/carbon/human/proc/RevConvert()
set name = "Rev-Convert"
set category = "IC"
+ var/list/Possible = list()
+ for (var/mob/living/carbon/human/P in oview(src))
+ if(!stat && P.client && P.mind && !P.mind.special_role)
+ Possible += P
+ if(!Possible.len)
+ src << "\red There doesn't appear to be anyone available for you to convert here."
+ return
+ var/mob/living/carbon/human/M = input("Select a person to convert", "Viva la revolution!", null) as mob in Possible
if(((src.mind in ticker.mode:head_revolutionaries) || (src.mind in ticker.mode:revolutionaries)))
if((M.mind in ticker.mode:head_revolutionaries) || (M.mind in ticker.mode:revolutionaries))
src << "\red [M] is already be a revolutionary!"
@@ -250,4 +259,4 @@
rev_obj.target = M.mind
rev_obj.explanation_text = "Assassinate, convert or capture [M.real_name], the [M.mind.assigned_role]."
rev_mind.objectives += rev_obj
- rev_mind.current << "\red A new Head of Staff, [M.real_name], the [M.mind.assigned_role] has appeared. Your objectives have been updated."
\ No newline at end of file
+ rev_mind.current << "\red A new Head of Staff, [M.real_name], the [M.mind.assigned_role] has appeared. Your objectives have been updated."
diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm
index 326060ee847..a9dd31c71b7 100644
--- a/code/game/gamemodes/setupgame.dm
+++ b/code/game/gamemodes/setupgame.dm
@@ -1,9 +1,13 @@
-/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS)
+/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS, var/good=0)
if(blocksLeft.len==0)
warning("[name]: No more blocks left to assign!")
return 0
var/assigned = pick(blocksLeft)
blocksLeft.Remove(assigned)
+ if(good)
+ good_blocks += assigned
+ else
+ bad_blocks += assigned
assigned_blocks[assigned]=name
dna_activity_bounds[assigned]=activity_bounds
//Debug message_admins("[name] assigned to block #[assigned].")
@@ -32,10 +36,10 @@
// Standard muts
BLINDBLOCK = getAssignedBlock("BLIND", numsToAssign)
DEAFBLOCK = getAssignedBlock("DEAF", numsToAssign)
- HULKBLOCK = getAssignedBlock("HULK", numsToAssign, DNA_HARD_BOUNDS)
- TELEBLOCK = getAssignedBlock("TELE", numsToAssign, DNA_HARD_BOUNDS)
- FIREBLOCK = getAssignedBlock("FIRE", numsToAssign, DNA_HARDER_BOUNDS)
- XRAYBLOCK = getAssignedBlock("XRAY", numsToAssign, DNA_HARDER_BOUNDS)
+ HULKBLOCK = getAssignedBlock("HULK", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ TELEBLOCK = getAssignedBlock("TELE", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ FIREBLOCK = getAssignedBlock("FIRE", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ XRAYBLOCK = getAssignedBlock("XRAY", numsToAssign, DNA_HARDER_BOUNDS, good=1)
CLUMSYBLOCK = getAssignedBlock("CLUMSY", numsToAssign)
FAKEBLOCK = getAssignedBlock("FAKE", numsToAssign)
COUGHBLOCK = getAssignedBlock("COUGH", numsToAssign)
@@ -46,17 +50,17 @@
// Bay muts
HEADACHEBLOCK = getAssignedBlock("HEADACHE", numsToAssign)
- NOBREATHBLOCK = getAssignedBlock("NOBREATH", numsToAssign, DNA_HARD_BOUNDS)
- REMOTEVIEWBLOCK = getAssignedBlock("REMOTEVIEW", numsToAssign, DNA_HARDER_BOUNDS)
- REGENERATEBLOCK = getAssignedBlock("REGENERATE", numsToAssign, DNA_HARDER_BOUNDS)
- INCREASERUNBLOCK = getAssignedBlock("INCREASERUN", numsToAssign, DNA_HARDER_BOUNDS)
- REMOTETALKBLOCK = getAssignedBlock("REMOTETALK", numsToAssign, DNA_HARDER_BOUNDS)
- MORPHBLOCK = getAssignedBlock("MORPH", numsToAssign, DNA_HARDER_BOUNDS)
- COLDBLOCK = getAssignedBlock("COLD", numsToAssign)
+ NOBREATHBLOCK = getAssignedBlock("NOBREATH", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ REMOTEVIEWBLOCK = getAssignedBlock("REMOTEVIEW", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ REGENERATEBLOCK = getAssignedBlock("REGENERATE", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ INCREASERUNBLOCK = getAssignedBlock("INCREASERUN", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ REMOTETALKBLOCK = getAssignedBlock("REMOTETALK", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ MORPHBLOCK = getAssignedBlock("MORPH", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ COLDBLOCK = getAssignedBlock("COLD", numsToAssign, good=1)
HALLUCINATIONBLOCK = getAssignedBlock("HALLUCINATION", numsToAssign)
- NOPRINTSBLOCK = getAssignedBlock("NOPRINTS", numsToAssign, DNA_HARD_BOUNDS)
- SHOCKIMMUNITYBLOCK = getAssignedBlock("SHOCKIMMUNITY", numsToAssign)
- SMALLSIZEBLOCK = getAssignedBlock("SMALLSIZE", numsToAssign, DNA_HARD_BOUNDS)
+ NOPRINTSBLOCK = getAssignedBlock("NOPRINTS", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ SHOCKIMMUNITYBLOCK = getAssignedBlock("SHOCKIMMUNITY", numsToAssign, good=1)
+ SMALLSIZEBLOCK = getAssignedBlock("SMALLSIZE", numsToAssign, DNA_HARD_BOUNDS, good=1)
//
// Goon muts
@@ -67,27 +71,26 @@
MUTEBLOCK = getAssignedBlock("MUTE", numsToAssign)
RADBLOCK = getAssignedBlock("RAD", numsToAssign)
FATBLOCK = getAssignedBlock("FAT", numsToAssign)
- STUTTERBLOCK = getAssignedBlock("STUTTER", numsToAssign)
CHAVBLOCK = getAssignedBlock("CHAV", numsToAssign)
SWEDEBLOCK = getAssignedBlock("SWEDE", numsToAssign)
SCRAMBLEBLOCK = getAssignedBlock("SCRAMBLE", numsToAssign)
- TOXICFARTBLOCK = getAssignedBlock("TOXICFART", numsToAssign)
- STRONGBLOCK = getAssignedBlock("STRONG", numsToAssign)
+ TOXICFARTBLOCK = getAssignedBlock("TOXICFART", numsToAssign, good=1)
+ STRONGBLOCK = getAssignedBlock("STRONG", numsToAssign, good=1)
HORNSBLOCK = getAssignedBlock("HORNS", numsToAssign)
// Powers
- SOBERBLOCK = getAssignedBlock("SOBER", numsToAssign)
- PSYRESISTBLOCK = getAssignedBlock("PSYRESIST", numsToAssign, DNA_HARD_BOUNDS)
- SHADOWBLOCK = getAssignedBlock("SHADOW", numsToAssign, DNA_HARDER_BOUNDS)
- CHAMELEONBLOCK = getAssignedBlock("CHAMELEON", numsToAssign, DNA_HARDER_BOUNDS)
- CRYOBLOCK = getAssignedBlock("CRYO", numsToAssign, DNA_HARD_BOUNDS)
- EATBLOCK = getAssignedBlock("EAT", numsToAssign, DNA_HARD_BOUNDS)
- JUMPBLOCK = getAssignedBlock("JUMP", numsToAssign, DNA_HARD_BOUNDS)
- MELTBLOCK = getAssignedBlock("MELT", numsToAssign)
- IMMOLATEBLOCK = getAssignedBlock("IMMOLATE", numsToAssign)
- EMPATHBLOCK = getAssignedBlock("EMPATH", numsToAssign, DNA_HARD_BOUNDS)
- SUPERFARTBLOCK = getAssignedBlock("SUPERFART", numsToAssign, DNA_HARDER_BOUNDS)
- POLYMORPHBLOCK = getAssignedBlock("POLYMORPH", numsToAssign, DNA_HARDER_BOUNDS)
+ SOBERBLOCK = getAssignedBlock("SOBER", numsToAssign, good=1)
+ PSYRESISTBLOCK = getAssignedBlock("PSYRESIST", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ SHADOWBLOCK = getAssignedBlock("SHADOW", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ CHAMELEONBLOCK = getAssignedBlock("CHAMELEON", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ CRYOBLOCK = getAssignedBlock("CRYO", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ EATBLOCK = getAssignedBlock("EAT", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ JUMPBLOCK = getAssignedBlock("JUMP", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ MELTBLOCK = getAssignedBlock("MELT", numsToAssign, good=1)
+ IMMOLATEBLOCK = getAssignedBlock("IMMOLATE", numsToAssign, good=1)
+ EMPATHBLOCK = getAssignedBlock("EMPATH", numsToAssign, DNA_HARD_BOUNDS, good=1)
+ SUPERFARTBLOCK = getAssignedBlock("SUPERFART", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ POLYMORPHBLOCK = getAssignedBlock("POLYMORPH", numsToAssign, DNA_HARDER_BOUNDS, good=1)
//
// Static Blocks
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index 310af6170b9..24198d4e776 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -8,7 +8,7 @@
name = "traitor"
config_tag = "traitor"
restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances
- protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative")//AI", Currently out of the list as malf does not work for shit
+ protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Internal Affairs Agent", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative")//AI", Currently out of the list as malf does not work for shit
required_players = 0
required_enemies = 1
recommended_enemies = 4
@@ -96,16 +96,23 @@
else
switch(rand(1,100))
- if(1 to 33)
+ if(1 to 25)
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = traitor
kill_objective.find_target()
traitor.objectives += kill_objective
- if(34 to 50)
- var/datum/objective/brig/brig_objective = new
- brig_objective.owner = traitor
- brig_objective.find_target()
- traitor.objectives += brig_objective
+ if(26 to 40)
+ var/datum/objective/debrain/debrain_objective = new
+ debrain_objective.owner = traitor
+ debrain_objective.find_target()
+ traitor.objectives += debrain_objective
+ if(41 to 50)
+ var/datum/objective/protect/protect_objective = new
+ protect_objective.owner = traitor
+ protect_objective.find_target_by_role("Traitor",0) //Protect your fellow traitor
+ if (!protect_objective.target)
+ protect_objective.find_target() //We could not find any traitors, protect somebody
+ traitor.objectives += protect_objective
if(51 to 66)
var/datum/objective/harm/harm_objective = new
harm_objective.owner = traitor
@@ -118,10 +125,27 @@
traitor.objectives += steal_objective
switch(rand(1,100))
if(1 to 30) // Die glorious death
- if (!(locate(/datum/objective/die) in traitor.objectives))
+ if (!(locate(/datum/objective/die) in traitor.objectives) && !(locate(/datum/objective/steal) in traitor.objectives))
var/datum/objective/die/die_objective = new
die_objective.owner = traitor
traitor.objectives += die_objective
+ else
+ if(prob(85))
+ if (!(locate(/datum/objective/escape) in traitor.objectives))
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = traitor
+ traitor.objectives += escape_objective
+ else
+ if(prob(50))
+ if (!(locate(/datum/objective/hijack) in traitor.objectives))
+ var/datum/objective/hijack/hijack_objective = new
+ hijack_objective.owner = traitor
+ traitor.objectives += hijack_objective
+ else
+ if (!(locate(/datum/objective/minimize_casualties) in traitor.objectives))
+ var/datum/objective/minimize_casualties/escape_objective = new
+ escape_objective.owner = traitor
+ traitor.objectives += escape_objective
if(31 to 90)
if (!(locate(/datum/objective/escape) in traitor.objectives))
var/datum/objective/escape/escape_objective = new
@@ -134,10 +158,11 @@
hijack_objective.owner = traitor
traitor.objectives += hijack_objective
else // Honk
- if (!(locate(/datum/objective/minimize_casualties) in traitor.objectives))
- var/datum/objective/minimize_casualties/escape_objective = new
- escape_objective.owner = traitor
- traitor.objectives += escape_objective
+ if (!(locate(/datum/objective/speciesist) in traitor.objectives))
+ var/datum/objective/speciesist/speciesist_objective = new
+ speciesist_objective.owner = traitor
+ speciesist_objective.find_target()
+ traitor.objectives += speciesist_objective
return
diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm
index 9540dd6880e..79a0966d860 100644
--- a/code/game/gamemodes/vampire/vampire_powers.dm
+++ b/code/game/gamemodes/vampire/vampire_powers.dm
@@ -220,7 +220,7 @@
C.ear_deaf = 20
C.stuttering = 20
C.Stun(8)
- C.make_jittery(150)
+ C.Jitter(150)
for(var/obj/structure/window/W in oview(3))
new W.shardtype(W.loc)
if(W.reinf) new /obj/item/stack/rods(W.loc)
@@ -278,7 +278,7 @@
if(!mind.vampire.iscloaking)
alpha = 255
return 0
- if(T.lighting_lumcount <= 2)
+ if(T.lit_value <= 2)
alpha = round((255 * 0.15))
return 1
else
@@ -432,7 +432,7 @@
if(T.y>world.maxy-outer_tele_radius || T.y max_lum) continue
+ if(T.lit_value > max_lum) continue
turfs += T
if(!turfs.len)
diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm
new file mode 100644
index 00000000000..a67cdc3231a
--- /dev/null
+++ b/code/game/gamemodes/wizard/raginmages.dm
@@ -0,0 +1,119 @@
+/datum/game_mode/wizard/raginmages
+ name = "Ragin' Mages"
+ config_tag = "raginmages"
+ required_players = 1
+ required_players_secret = 15
+ var/max_mages = 0
+ var/making_mage = 0
+ var/mages_made = 1
+ var/time_checked = 0
+
+/datum/game_mode/wizard/announce()
+ world << "The current game mode is - Ragin' Mages!"
+ world << "The \red Space Wizard Federation\black is pissed, help defeat all the space wizards!"
+
+/datum/game_mode/wizard/raginmages/post_setup()
+ var/playercount = 0
+ ..()
+ if(!max_mages)
+ for(var/mob/living/player in mob_list)
+ if (player.client && player.stat != 2)
+ playercount += 1
+ max_mages = round(playercount / 5)
+
+/datum/game_mode/wizard/raginmages/greet_wizard(var/datum/mind/wizard, var/you_are=1)
+ if (you_are)
+ wizard.current << "\red You are the Space Wizard!"
+ wizard.current << "The Space Wizards Federation has given you the following tasks:"
+
+ var/obj_count = 1
+ for(var/datum/objective/objective in wizard.objectives)
+ wizard.current << "Objective #[obj_count]: [objective.explanation_text]"
+ obj_count++
+ wizard.current << "Objective Alpha: Make sure the station pays for its actions against our diplomats"
+ return
+
+/datum/game_mode/wizard/raginmages/check_finished()
+ var/wizards_alive = 0
+ for(var/datum/mind/wizard in wizards)
+ if(!istype(wizard.current,/mob/living/carbon))
+ continue
+ if(istype(wizard.current,/mob/living/carbon/brain))
+ continue
+ if(wizard.current.stat==2)
+ continue
+ if(wizard.current.stat==1)
+ if(wizard.current.health < 0)
+ wizard.current << "\red The Space Wizard Federation is upset with your performance and have terminated your employment."
+ wizard.current.stat = 2
+ continue
+ wizards_alive++
+
+ if (wizards_alive)
+ if(!time_checked) time_checked = world.time
+ if(world.time > time_checked + 3000 && (mages_made < max_mages))
+ time_checked = world.time
+ make_more_mages()
+ else
+ if(wizards.len >= max_mages)
+ finished = 1
+ return 1
+ else
+ make_more_mages()
+ return ..()
+
+/datum/game_mode/wizard/raginmages/proc/make_more_mages()
+
+ if(making_mage || emergency_shuttle.departed)
+ return 0
+ if(mages_made >= max_mages)
+ return 0
+ making_mage = 1
+ mages_made++
+ var/list/mob/dead/observer/candidates = list()
+ var/mob/dead/observer/theghost = null
+ spawn(rand(200, 600))
+ message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.")
+ for(var/mob/dead/observer/G in player_list)
+ if(G.client && !G.client.holder && !G.client.is_afk() && G.client.prefs.be_special & BE_WIZARD)
+ if(!jobban_isbanned(G, "wizard") && !jobban_isbanned(G, "Syndicate"))
+ candidates += G
+ if(!candidates.len)
+ message_admins("No applicable ghosts for the next ragin' mage, asking ghosts instead.")
+ var/time_passed = world.time
+ for(var/mob/dead/observer/G in player_list)
+ if(!jobban_isbanned(G, "wizard") && !jobban_isbanned(G, "Syndicate"))
+ spawn(0)
+ switch(alert(G, "Do you wish to be considered for the position of Space Wizard Foundation 'diplomat'?","Please answer in 30 seconds!","Yes","No"))
+ if("Yes")
+ if((world.time-time_passed)>300)//If more than 30 game seconds passed.
+ continue
+ candidates += G
+ if("No")
+ continue
+
+ sleep(300)
+ if(!candidates.len)
+ message_admins("This is awkward, sleeping until another mage check...")
+ making_mage = 0
+ mages_made--
+ return
+ else
+ shuffle(candidates)
+ for(var/mob/i in candidates)
+ if(!i || !i.client) continue //Dont bother removing them from the list since we only grab one wizard
+
+ theghost = i
+ break
+
+ if(theghost)
+ var/mob/living/carbon/human/new_character= makeBody(theghost)
+ new_character.mind.make_Wizard()
+ making_mage = 0
+ return 1
+
+/datum/game_mode/wizard/raginmages/declare_completion()
+ if(finished)
+ feedback_set_details("round_end_result","loss - wizard killed")
+ world << "\red The crew has managed to hold off the wizard attack! The Space Wizards Federation has been taught a lesson they will not soon forget!"
+ ..(1)
\ No newline at end of file
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index 6bc076304c6..76af27e3fc8 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -211,8 +211,8 @@
-/datum/game_mode/wizard/declare_completion()
- if(finished)
+/datum/game_mode/wizard/declare_completion(var/ragin = 0)
+ if(finished && !ragin)
feedback_set_details("round_end_result","loss - wizard killed")
world << "\red The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!"
..()
diff --git a/code/game/gamemodes/xenos/xenos.dm b/code/game/gamemodes/xenos/xenos.dm
index 60eb9d05896..54d63a6948b 100644
--- a/code/game/gamemodes/xenos/xenos.dm
+++ b/code/game/gamemodes/xenos/xenos.dm
@@ -5,11 +5,11 @@
/datum/game_mode/xenos
name = "xenos"
config_tag = "xenos"
- required_players = 15
- recommended_players = 20
- required_players_secret = 15
- required_enemies = 3
- recommended_enemies = 5
+ required_players = 0
+ recommended_players = 30
+ required_players_secret = 20
+ required_enemies = 5
+ recommended_enemies = 8
var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds)
var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds)
@@ -74,10 +74,9 @@
for(var/datum/mind/xeno_mind in xenos)
if(spawnpos > xenos_spawn.len)
spawnpos = 1
- xeno_mind.current.loc = xenos_spawn[spawnpos]
//XenoAI selection
if(!xenoai_selected)
- var/mob/living/silicon/ai/O = new(xeno_mind.current.loc,,,1)//No MMI but safety is in effect.
+ var/mob/living/silicon/ai/O = new(xenos_spawn[spawnpos],,,1)//No MMI but safety is in effect.
O.invisibility = 0
O.aiRestorePowerRoutine = 0
@@ -93,15 +92,23 @@
loc_landmark = sloc
O.loc = loc_landmark.loc
O.icon_state = "ai-alien"
- O.verbs.Remove(/mob/living/silicon/ai/verb/pick_icon)
+ O.verbs.Remove(,/mob/living/silicon/ai/proc/ai_call_shuttle,/mob/living/silicon/ai/proc/ai_camera_track, \
+ /mob/living/silicon/ai/proc/ai_camera_list, /mob/living/silicon/ai/proc/ai_network_change, \
+ /mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
+ /mob/living/silicon/ai/proc/toggle_camera_light,/mob/living/silicon/ai/verb/pick_icon)
O.laws = new /datum/ai_laws/alienmov
O.name = "Alien AI"
+ O.real_name = name
+ O.alienAI = 1
+ O.network = list("SS13","Xeno")
+ O.holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo3"))
+ O.alien_talk_understand = 1
xenoai_selected = 1
spawnpos++
continue
//XenoQueen Selection
if(!xenoqueen_selected)
- var/mob/living/carbon/alien/humanoid/queen/large/O = new(xeno_mind.current.loc)
+ var/mob/living/carbon/alien/humanoid/queen/large/O = new(xenos_spawn[spawnpos])
if(xeno_mind.current)
xeno_mind.transfer_to(O)
else
@@ -112,7 +119,7 @@
continue
//XenoBorg Selection
if(!xenoborg_selected)
- var/mob/living/silicon/robot/O = new(xeno_mind.current.loc,0,0,1)
+ var/mob/living/silicon/robot/O = new(xenos_spawn[spawnpos],0,0,1)
O.mmi = new /obj/item/device/mmi(O)
O.mmi.alien = 1
O.mmi.transfer_identity(xeno_mind.current)//Does not transfer key/client.
@@ -126,11 +133,16 @@
del(xeno_mind)
O.job = "Alien Cyborg"
O.name = "Alien Cyborg"
+ O.real_name = name
O.module = new /obj/item/weapon/robot_module/alien/hunter(src)
O.hands.icon_state = "standard"
- O.icon = "icons/mob/alien.dmi"
+ O.icon = 'icons/mob/alien.dmi'
O.icon_state = "xenoborg-state-a"
O.modtype = "Xeno-Hu"
+ O.connected_ai = select_active_alien_ai()
+ O.laws = new /datum/ai_laws/alienmov()
+ O.scrambledcodes = 1
+ O.alien_talk_understand = 1
feedback_inc("xeborg_hunter",1)
@@ -139,7 +151,7 @@
continue
//Additional larvas if playercount > 20
else
- var/mob/living/carbon/alien/larva/O = new(xeno_mind.current.loc)
+ var/mob/living/carbon/alien/larva/O = new(xenos_spawn[spawnpos])
if(xeno_mind.current)
xeno_mind.transfer_to(O)
else
diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm
index 39e5c42244b..5e2b13e73d0 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -68,6 +68,7 @@
/var/const/access_paramedic = 66
/var/const/access_blueshield = 67
/var/const/access_customs = 68
+/var/const/access_salvage_captain = 69 // Salvage ship captain's quarters
//BEGIN CENTCOM ACCESS
/*Should leave plenty of room if we need to add more access levels.
@@ -93,29 +94,6 @@
/obj/var/list/req_one_access = null
/obj/var/req_one_access_txt = "0"
-/obj/New()
- ..()
- //NOTE: If a room requires more than one access (IE: Morgue + medbay) set the req_acesss_txt to "5;6" if it requires 5 and 6
- if(src.req_access_txt)
- var/list/req_access_str = text2list(req_access_txt,";")
- if(!req_access)
- req_access = list()
- for(var/x in req_access_str)
- var/n = text2num(x)
- if(n)
- req_access += n
-
- if(src.req_one_access_txt)
- var/list/req_one_access_str = text2list(req_one_access_txt,";")
- if(!req_one_access)
- req_one_access = list()
- for(var/x in req_one_access_str)
- var/n = text2num(x)
- if(n)
- req_one_access += n
-
-
-
//returns 1 if this mob has sufficient access to use this object
/obj/proc/allowed(mob/M)
//check if it doesn't require any access at all
@@ -143,9 +121,25 @@
return null
/obj/proc/check_access(obj/item/I)
+ //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system.
+ if(!src.req_access)
+ src.req_access = list()
+ if(src.req_access_txt)
+ var/list/req_access_str = text2list(req_access_txt,";")
+ for(var/x in req_access_str)
+ var/n = text2num(x)
+ if(n)
+ req_access += n
+
+ if(!src.req_one_access)
+ src.req_one_access = list()
+ if(src.req_one_access_txt)
+ var/list/req_one_access_str = text2list(req_one_access_txt,";")
+ for(var/x in req_one_access_str)
+ var/n = text2num(x)
+ if(n)
+ req_one_access += n
- if(!src.req_access && !src.req_one_access) //no requirements
- return 1
if(!istype(src.req_access, /list)) //something's very wrong
return 1
@@ -449,10 +443,10 @@
rank = src:rank
assignment = src:assignment
- if( rank in get_all_jobs() )
+ if( rank in joblist )
return rank
- if( assignment in get_all_jobs() )
+ if( assignment in joblist )
return assignment
return "Unknown"
@@ -505,7 +499,7 @@ proc/FindNameFromID(var/mob/living/carbon/human/H)
return ID.registered_name
proc/get_all_job_icons() //For all existing HUD icons
- return get_all_jobs() + list("Prisoner")
+ return joblist + list("Prisoner")
/obj/proc/GetJobName() //Used in secHUD icon generation
if (!istype(src, /obj/item/device/pda) && !istype(src,/obj/item/weapon/card/id))
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 843f1108c9f..0814dba74e6 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -453,6 +453,10 @@ var/global/datum/controller/occupations/job_master
var/obj/item/clothing/glasses/G = H.glasses
G.prescription = 1
// H.update_icons()
+
+ H.hud_updateflag |= (1 << ID_HUD)
+ H.hud_updateflag |= (1 << IMPLOYAL_HUD)
+ H.hud_updateflag |= (1 << SPECIALROLE_HUD)
return 1
diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm
index df5898b35f0..b7e76239428 100644
--- a/code/game/jobs/whitelist.dm
+++ b/code/game/jobs/whitelist.dm
@@ -62,7 +62,7 @@ proc/load_alienwhitelist()
usr << "\red Unable to connect to whitelist database. Please try again later.
"
return 0
else
- var/DBQuery/query = dbcon.NewQuery("SELECT species FROM whitelist WHERE ckey='[M.ckey]'")
+ var/DBQuery/query = dbcon.NewQuery("SELECT species FROM whitelist WHERE ckey='[M.key]'")
query.Execute()
while(query.NextRow())
diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm
index 37cc168d572..3e1d2cd2b93 100644
--- a/code/game/machinery/Beacon.dm
+++ b/code/game/machinery/Beacon.dm
@@ -20,7 +20,7 @@
hide(T.intact)
- Del()
+ Destroy()
if(Beacon)
del(Beacon)
..()
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 03a67b4f719..0be949dbb01 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -187,7 +187,7 @@
blob_act()
if(prob(75))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
A.blob_act()
del(src)
return
@@ -198,7 +198,7 @@
if(!beaker)
beaker = G
user.drop_item()
- G.loc = src
+ G.setloc(src)
user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!")
src.updateUsrDialog()
return
@@ -228,7 +228,7 @@
if(M.client)
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
- M.loc = src
+ M.setloc(src)
src.occupant = M
src.icon_state = "sleeper_1"
if(orient == "RIGHT")
@@ -248,21 +248,21 @@
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
qdel(src)
return
if(2.0)
if(prob(50))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
qdel(src)
return
if(3.0)
if(prob(25))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
qdel(src)
return
@@ -309,7 +309,7 @@
if(src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
- src.occupant.loc = src.loc
+ src.occupant.setloc(src.loc)
src.occupant = null
if(orient == "RIGHT")
icon_state = "sleeper_0-r"
@@ -411,8 +411,29 @@
if(orient == "RIGHT")
icon_state = "sleeper_1-r"
- for(var/obj/O in src)
- del(O)
- src.add_fingerprint(usr)
+ for(var/mob/living/carbon/slime/M in range(1,usr))
+ if(M.Victim == usr)
+ usr << "You're too busy getting your life sucked out of you."
+ return
+ visible_message("[usr] starts climbing into the sleeper.", 3)
+ if(do_after(usr, 20))
+ if(src.occupant)
+ usr << "\blue The sleeper is already occupied!"
+ return
+ usr.stop_pulling()
+ usr.client.perspective = EYE_PERSPECTIVE
+ usr.client.eye = src
+ usr.setloc(src)
+ src.occupant = usr
+ src.icon_state = "sleeper_1"
+ if(orient == "RIGHT")
+ icon_state = "sleeper_1-r"
+
+ usr << "\blue You feel cool air surround you. You go numb as your senses turn inward."
+
+ for(var/obj/O in src)
+ del(O)
+ src.add_fingerprint(usr)
+ return
return
- return
\ No newline at end of file
+ return
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 34afcf5e97b..adc0a84ade5 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -46,7 +46,7 @@
usr.pulling = null
usr.client.perspective = EYE_PERSPECTIVE
usr.client.eye = src
- usr.loc = src
+ usr.setloc(src)
src.occupant = usr
src.icon_state = "body_scanner_1"
for(var/obj/O in src)
@@ -60,12 +60,12 @@
if ((!( src.occupant ) || src.locked))
return
for(var/obj/O in src)
- O.loc = src.loc
+ O.setloc(src.loc)
//Foreach goto(30)
if (src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
- src.occupant.loc = src.loc
+ src.occupant.setloc(src.loc)
src.occupant = null
src.icon_state = "body_scanner_0"
return
@@ -83,11 +83,11 @@
if (M.client)
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
- M.loc = src
+ M.setloc(src)
src.occupant = M
src.icon_state = "body_scanner_1"
for(var/obj/O in src)
- O.loc = src.loc
+ O.setloc(src.loc)
//Foreach goto(154)
src.add_fingerprint(user)
//G = null
@@ -98,7 +98,7 @@
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
//Foreach goto(35)
//SN src = null
@@ -107,7 +107,7 @@
if(2.0)
if (prob(50))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
//Foreach goto(108)
//SN src = null
@@ -116,7 +116,7 @@
if(3.0)
if (prob(25))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
ex_act(severity)
//Foreach goto(181)
//SN src = null
@@ -128,7 +128,7 @@
/obj/machinery/bodyscanner/blob_act()
if(prob(50))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.setloc(src.loc)
del(src)
/obj/machinery/body_scanconsole/ex_act(severity)
@@ -180,10 +180,12 @@
return
return
+/*
+
/obj/machinery/body_scanconsole/process() //not really used right now
if(stat & (NOPOWER|BROKEN))
return
- use_power(250) // power stuff
+ //use_power(250) // power stuff
// var/mob/M //occupant
// if (!( src.status )) //remove this
@@ -199,6 +201,8 @@
// src.updateDialog()
// return
+*/
+
/obj/machinery/body_scanconsole/attack_paw(user as mob)
return src.attack_hand(user)
diff --git a/code/game/machinery/airlock_control.dm b/code/game/machinery/airlock_control.dm
index ae60ec8cb34..02cc0222cfe 100644
--- a/code/game/machinery/airlock_control.dm
+++ b/code/game/machinery/airlock_control.dm
@@ -90,7 +90,7 @@ obj/machinery/door/airlock/Bumped(atom/AM)
radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
return
-
+
obj/machinery/door/airlock/proc/set_frequency(new_frequency)
radio_controller.remove_object(src, frequency)
if(new_frequency)
@@ -130,7 +130,7 @@ obj/machinery/airlock_sensor
var/on = 1
var/alert = 0
-
+ var/previousPressure
obj/machinery/airlock_sensor/update_icon()
if(on)
@@ -152,21 +152,23 @@ obj/machinery/airlock_sensor/attack_hand(mob/user)
obj/machinery/airlock_sensor/process()
if(on)
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.data["tag"] = id_tag
- signal.data["timestamp"] = world.time
-
var/datum/gas_mixture/air_sample = return_air()
-
var/pressure = round(air_sample.return_pressure(),0.1)
- alert = (pressure < ONE_ATMOSPHERE*0.8)
- signal.data["pressure"] = num2text(pressure)
+ if(abs(pressure - previousPressure) > 0.001 || previousPressure == null)
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.data["tag"] = id_tag
+ signal.data["timestamp"] = world.time
+ signal.data["pressure"] = num2text(pressure)
- radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
+ radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
- update_icon()
+ previousPressure = pressure
+
+ alert = (pressure < ONE_ATMOSPHERE*0.8)
+
+ update_icon()
obj/machinery/airlock_sensor/proc/set_frequency(new_frequency)
radio_controller.remove_object(src, frequency)
@@ -178,7 +180,6 @@ obj/machinery/airlock_sensor/initialize()
obj/machinery/airlock_sensor/New()
..()
-
if(radio_controller)
set_frequency(frequency)
@@ -238,4 +239,4 @@ obj/machinery/access_button/New()
..()
if(radio_controller)
- set_frequency(frequency)
\ No newline at end of file
+ set_frequency(frequency)
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 1e42420be6d..271e2ff4a02 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -1043,6 +1043,13 @@ FIRE ALARM
var/last_process = 0
var/wiresexposed = 0
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
+ var/area/master_area
+
+/obj/machinery/firealarm/New()
+ var/area/A = get_area_master(src)
+ if (!( istype(A, /area) ))
+ return
+ master_area=A
/obj/machinery/firealarm/update_icon()
@@ -1368,6 +1375,14 @@ Code shamelessly copied from apc_frame
idle_power_usage = 2
active_power_usage = 6
+ var/area/master_area
+
+/obj/machinery/partyalarm/New()
+ var/area/A = get_area_master(src)
+ if (!( istype(A, /area) ))
+ return
+ master_area=A
+
/obj/machinery/partyalarm/attack_paw(mob/user as mob)
return attack_hand(user)
diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm
index bf2c9c3e1f8..bb35302386c 100644
--- a/code/game/machinery/atmo_control.dm
+++ b/code/game/machinery/atmo_control.dm
@@ -90,16 +90,15 @@ obj/machinery/air_sensor
if(..(user))
return
var/html=return_text()+"