Merge pull request #9 from ZomgPonies/master

Update
This commit is contained in:
SamCroswell
2014-04-01 12:47:14 -04:00
1358 changed files with 64569 additions and 18329 deletions
@@ -44,7 +44,7 @@ obj/machinery/atmospherics/binary
return null
Del()
Destroy()
loc = null
if(node1)
@@ -62,7 +62,7 @@
return null
Del()
Destroy()
loc = null
if(connected_device)
@@ -52,7 +52,7 @@ obj/machinery/atmospherics/trinary
return null
Del()
Destroy()
loc = null
if(node1)
+1 -1
View File
@@ -78,7 +78,7 @@ obj/machinery/atmospherics/tvalve
return null
Del()
Destroy()
loc = null
if(node1)
@@ -28,7 +28,7 @@
return null
Del()
Destroy()
loc = null
if(node)
@@ -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
@@ -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
+1 -1
View File
@@ -62,7 +62,7 @@ obj/machinery/atmospherics/valve
return null
Del()
Destroy()
loc = null
if(node1)
+7 -7
View File
@@ -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)
@@ -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)
@@ -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 << "<b><font color=red>Processing lights...</font></b>"
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 << "<font color=red>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 << "<b><font color=red>Lighting initialization took [(world.timeofday-start_time)/world.fps] seconds.</font></b>"
world << "<font color=red>Updated [turfs_updated] turfs.</font>"
/datum/controller/lighting/proc/GetLightIcon(a,b,c,d)
if(a <= 2) return LIGHT_ICON_1
else return LIGHT_ICON_2
+231
View File
@@ -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
@@ -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
@@ -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
@@ -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 << "<b>[src]</b>"
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 << "<b>[src]</b>"
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
@@ -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))
@@ -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
@@ -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()
@@ -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)
@@ -120,7 +120,7 @@
return
return
/obj/beam/e_beam/Del()
/obj/beam/e_beam/Destroy()
if(next)
del(next)
..()
+4 -1
View File
@@ -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
@@ -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"
@@ -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
+1 -1
View File
@@ -122,7 +122,7 @@
else
stat &= ~NOPOWER
Del()
Destroy()
if(istype(loc,/obj/item/device/laptop))
var/obj/O = loc
spawn(5)
@@ -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
@@ -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 << "<span class='notice'>You [hatch_open ? "open" : "close"] the maintenance hatch.</span>"
if(istype(W, /obj/item/weapon/cell))
if(!hatch_open)
return ..()
if(battery)
user << "<span class='notice'>The pod already has a battery.</span>"
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 << "<span class='warning'>The pod has no equipment datum, yell at pomf</span>"
return
if(istype(W, /obj/item/device/spacepod_equipment/weaponry))
if(equipment_system.weapon_system)
user << "<span class='notice'>The pod already has a weapon system, remove it first.</span>"
return
else
user << "<span class='notice'>You insert \the [W] into the equipment system.</span>"
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 << "<span class='warning'>The pod has no equpment datum, or is the wrong type, yell at pomf.</span>"
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 << "<span class='notice'>You remove \the [battery] from the space pod</span>"
battery = null
if("Weapon System")
SPE = equipment_system.weapon_system
if(user.put_in_any_hand_if_possible(SPE))
user << "<span class='notice'>You remove \the [SPE] from the equipment system.</span>"
equipment_system.weapon_system = null
else
user << "<span class='warning'>You need an open hand to do that.</span>"
/*
if("engine system")
SPE = equipment_system.engine_system
if(user.put_in_any_hand_if_possible(SPE))
user << "<span class='notice'>You remove \the [SPE] from the equipment system.</span>"
equipment_system.engine_system = null
else
user << "<span class='warning'>You need an open hand to do that.</span>"
if("shield system")
SPE = equipment_system.shield_system
if(user.put_in_any_hand_if_possible(SPE))
user << "<span class='notice'>You remove \the [SPE] from the equipment system.</span>"
equipment_system.shield_system = null
else
user << "<span class='warning'>You need an open hand to do that.</span>"
*/
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 << "<span class='notice'>Now taking air from [use_internal_tank?"internal airtank":"environment"].</span>"
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 << "<span class='warning'>Your weapons are recharging.</span>"
return
var/turf/firstloc
var/turf/secondloc
if(!equipment_system || !equipment_system.weapon_system)
usr << "<span class='warning'>Missing equipment or weapons.</span>"
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 <B>The [src.name] is already occupied!</B>"
return
/*
if (usr.abiotic())
usr << "\blue <B>Subject cannot have abiotic items on.</B>"
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 << "<span class='notice'>You climb out of the pod</span>"
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<numticks, i++)
sleep(delayfraction)
if(!src || !user || !user.canmove || !(user.loc == T))
return 0
return 1
/datum/global_iterator/pod_preserve_temp //normalizing cabin air temperature to 20 degrees celsium
delay = 20
process(var/obj/spacepod/spacepod)
if(spacepod.cabin_air && spacepod.cabin_air.return_volume() > 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 << "<span class='warning'>She's dead, Jim</span>"
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()
..()
+420
View File
@@ -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 <B>\The [src] slams into \a [A]!</B>",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 <B>\The [src] slams into \a [A]!</B>",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 <B>[src] slams into [A]!</B>",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
+448
View File
@@ -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
+219
View File
@@ -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 << "<u>Zone Air Contents</u>"
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 << "<u>Connections: [length(connections)]</u>"
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)
+362
View File
@@ -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")
+163
View File
@@ -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()
+336
View File
@@ -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 += "<b>[vw_name] = [vw]</b> <A href='?src=\ref[src];changevar=[ch]'>\[Change\]</A><br>"
dat += "<i>[vw_desc]</i><br><br>"
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 <b>[key_name(user)] changed the setting [display_description] to [newvar].</b>"
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 <b>[key_name(user)] changed the global plasma/ZAS settings to \"[def]\"</b>"
/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
@@ -118,7 +118,7 @@
. = ..()
/turf/simulated/Del()
/turf/simulated/Destroy()
if(active_hotspot)
del(active_hotspot)
if(blocks_air)
+4 -168
View File
@@ -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
+66
View File
@@ -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
+141 -424
View File
@@ -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."
+427
View File
@@ -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)
+102
View File
@@ -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()
+321
View File
@@ -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 << {"<font color='red'><b>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</b>
Total Simulated Turfs: [simulated_turf_count]
Total Zones: [zones.len]
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]</font>"}
// 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)
+17 -216
View File
@@ -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 << "<u>Zone Air Contents</u>"
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 << "<u>Connections: [length(connections)]</u>"
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)
proc/soft_assert(thing,fail)
if(!thing) message_admins(fail)
+236
View File
@@ -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 << "<u>Zone Air Contents</u>"
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 << "<u>Connections: [length(connections)]</u>"
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)
+9 -15
View File
@@ -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
+241
View File
@@ -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
+154
View File
@@ -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])"
+35
View File
@@ -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
File diff suppressed because it is too large Load Diff
+105 -11
View File
@@ -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
+8 -2
View File
@@ -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
+7
View File
@@ -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
+23 -1
View File
@@ -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](<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[target.x];Y=[target.y];Z=[target.z]'>JMP</a>)")
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"
+3 -3
View File
@@ -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*")
+4
View File
@@ -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"
+54
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
/hook/login
name = "Login"
+2
View File
@@ -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)
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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")
+12 -17
View File
@@ -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:
+17
View File
@@ -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)
+2 -2
View File
@@ -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()
..()
+6
View File
@@ -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]'"
+3 -2
View File
@@ -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
+49 -4
View File
@@ -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<min(destroyed.len,GC_COLLECTIONS_PER_TICK);i++)
if(destroyed.len)
var/refID=destroyed[1]
var/atom/A = locate(refID)
if(A && A.gc_destroyed && A.gc_destroyed >= 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)
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 << "<b>GC: qdel turned [garbage.del_everything?"off":"on"].</b>"
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)
+20 -2
View File
@@ -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
+5 -2
View File
@@ -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")
+6 -1
View File
@@ -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
return preview_icon
+1 -1
View File
@@ -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)
*/
@@ -33,5 +33,5 @@ Bonus
M << "<span class='notice'>[pick("You feel dizzy.", "Your head starts spinning.")]</span>"
else
M << "<span class='notice'>You are unable to look straight!</span>"
M.make_dizzy(5)
M.Dizzy(5)
return
+23 -4
View File
@@ -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 <Font size =3><B>Your loyalty implant has been deactivated.</B></FONT>"
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)
+4 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+19 -17
View File
@@ -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)
+1 -2
View File
@@ -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,
+11 -1
View File
@@ -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"
+14 -1
View File
@@ -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',
+63 -10
View File
@@ -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
+52 -44
View File
@@ -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)
+27
View File
@@ -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
+12
View File
@@ -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
+5 -7
View File
@@ -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)
+3
View File
@@ -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
/////////////////
+2 -2
View File
@@ -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
+11 -14
View File
@@ -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
+10 -8
View File
@@ -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 <b>[L.name]</b> 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 <b>[L.name]</b> suddenly bursts into flames!")
//playsound(L.loc, 'mag_fireballlaunch.ogg', 50, 0)
////////////////////////////////////////////////////////////////////////
+74 -25
View File
@@ -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 << "<span class='notice'>No target found in range.</span>"
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 << "<span class='notice'>No target found in range.</span>"
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 <b>[usr] begins stuffing [the_item]'s [limb.display_name] into [m_his] gaping maw!</b>")
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
+1 -1
View File
@@ -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
+9 -6
View File
@@ -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.")
+83
View File
@@ -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
@@ -133,6 +133,7 @@
newtraitor << "\red <B>ATTENTION:</B> \black It is time to pay your debt to the Syndicate..."
newtraitor << "<B>You are now a traitor.</B>"
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)
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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 <b>You have detected a change in your laws information:</b>"
aiPlayer << "Laws Updated: [law]"
+1 -4
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -4
View File
@@ -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)
..()
+1 -4
View File
@@ -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
+48 -6
View File
@@ -26,21 +26,61 @@
sync_mind()
src << "<span class='notice'>You are the overmind!</span>"
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 << "<b>Normal Blob</b> will expand your reach and allow you to upgrade into special blobs that perform certain functions."
src << "<b>Shield Blob</b> 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 << "<b>Resource Blob</b> 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 << "<b>Node Blob</b> 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 << "<b>Factory Blob</b> 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 << "<b>Shortcuts:</b> 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 = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='#e36600'>[blob_core.health]</font></div>"
mob/camera/blob/Life()
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='#82ed00'>[src.blob_points]</font></div>"
hud_used.blobhealthdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='#e36600'>[blob_core.health]</font></div>"
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 = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='#82ed00'>[src.blob_points]</font></div>"
/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 = "<font color=\"#EE4000\"><i><span class='game say'>Blob Telepathy, <span class='name'>[name]</span> <span class='message'>[message_a]</span></span></i></font>"
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 = "<font color=\"#EE4000\"><i><span class='game say'>Blob Telepathy, <span class='name'>[name]</span> <a href='byond://?src=\ref[M];follow2=\ref[M];follow=\ref[src]'>(Follow)</a> <span class='message'>[message_a]</span></span></i></font>"
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

Some files were not shown because too many files have changed in this diff Show More