mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-04 06:22:14 +00:00
Merged Upstream
This is a big one.
This commit is contained in:
45
code/WorkInProgress/Aryn/Lighting/Compatibility.dm
Normal file
45
code/WorkInProgress/Aryn/Lighting/Compatibility.dm
Normal file
@@ -0,0 +1,45 @@
|
||||
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)
|
||||
n = min(n,10) //Caelcode.
|
||||
if(n > 0)
|
||||
SetLight(max(1,n>>1),n)
|
||||
else
|
||||
SetLight(0,0)
|
||||
luminosity = n
|
||||
//else lighting_controller.initial_lights.Add(src)
|
||||
119
code/WorkInProgress/Aryn/Lighting/Controller.dm
Normal file
119
code/WorkInProgress/Aryn/Lighting/Controller.dm
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
|
||||
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 = 4
|
||||
var/list/icon_updates = list()
|
||||
|
||||
var/list/light_border = 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.light_overlay && !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.UpdateLight()
|
||||
|
||||
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/MarkIconUpdate(turf/T)
|
||||
if(!T.needs_light_update)
|
||||
icon_updates.Add(T)
|
||||
T.needs_light_update = 1
|
||||
|
||||
/datum/controller/lighting/proc/FlushIconUpdates()
|
||||
for(var/turf/T in icon_updates)
|
||||
T.UpdateLight()
|
||||
T.needs_light_update = 0
|
||||
icon_updates = list()
|
||||
|
||||
/datum/controller/lighting/proc/AddBorder(turf/T)
|
||||
if(!T.is_border)
|
||||
light_border.Add(T)
|
||||
T.is_border = 1
|
||||
//T.overlays.Add(border)
|
||||
|
||||
/datum/controller/lighting/proc/RemoveBorder(turf/T)
|
||||
if(T.is_border)
|
||||
light_border.Remove(T)
|
||||
T.is_border = 0
|
||||
218
code/WorkInProgress/Aryn/Lighting/Engine.dm
Normal file
218
code/WorkInProgress/Aryn/Lighting/Engine.dm
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
#define LIGHTCLAMP(x) ( max(0,min(3,round(x,1))) )
|
||||
|
||||
obj/effect/lighting_overlay
|
||||
//anchored = 1
|
||||
layer = 9
|
||||
mouse_opacity = 0
|
||||
icon = 'icons/effects/ArynLights.dmi'
|
||||
icon_state = "0000"
|
||||
invisibility = INVISIBILITY_LIGHTING
|
||||
|
||||
atom/var/light/light
|
||||
|
||||
turf/var/obj/effect/lighting_overlay/light_overlay
|
||||
|
||||
turf/var/lit_value = 0
|
||||
turf/var/max_brightness = 0
|
||||
turf/var/has_opaque = 0
|
||||
turf/var/is_outside = 0
|
||||
turf/var/is_border = 0
|
||||
turf/var/needs_light_update = 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.Reset()
|
||||
if(opacity)
|
||||
if(lighting_ready())
|
||||
opacity = 0
|
||||
SetOpacity(1)
|
||||
|
||||
atom/movable/Move()
|
||||
var/o = opacity
|
||||
if(o) SetOpacity(0)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(o) SetOpacity(1)
|
||||
if(light)
|
||||
light.Reset()
|
||||
if(lighting_ready()) lighting_controller.FlushIconUpdates()
|
||||
|
||||
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))
|
||||
for(var/light/A in T.lit_by)
|
||||
A.Reset()
|
||||
lighting_controller.FlushIconUpdates()
|
||||
|
||||
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)
|
||||
if(is_outside) return
|
||||
|
||||
var/brightness = light.CalculateBrightness(src)
|
||||
if(brightness <= 0) return
|
||||
|
||||
if(!lit_by) lit_by = list()
|
||||
lit_by.Add(light)
|
||||
|
||||
|
||||
lit_by[light] = brightness
|
||||
|
||||
if(!has_opaque && lighting_ready())
|
||||
if(brightness > max_brightness)
|
||||
lit_value = LIGHTCLAMP(brightness)
|
||||
max_brightness = brightness
|
||||
ResetCachedValues()
|
||||
for(var/turf/T in range(1,src))
|
||||
lighting_controller.MarkIconUpdate(T)
|
||||
|
||||
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/proc/ResetValue()
|
||||
if(is_outside)
|
||||
max_brightness = lighting_controller.starlight
|
||||
lit_value = LIGHTCLAMP(lighting_controller.starlight)
|
||||
return
|
||||
|
||||
CheckForOpaqueObjects()
|
||||
if(has_opaque)
|
||||
lit_value = 0
|
||||
else
|
||||
the_part_where_I_calculate_brightness()
|
||||
|
||||
if(lighting_ready())
|
||||
the_part_where_I_use_range()
|
||||
|
||||
turf/proc
|
||||
the_part_where_I_calculate_brightness()
|
||||
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)
|
||||
|
||||
the_part_where_I_use_range()
|
||||
ResetCachedValues()
|
||||
for(var/turf/T in range(1,src))
|
||||
lighting_controller.MarkIconUpdate(T)
|
||||
|
||||
turf/proc/ResetCachedValues()
|
||||
if(lightNE)
|
||||
lightNE.cached_value = -1
|
||||
if(lightNW)
|
||||
lightNW.cached_value = -1
|
||||
if(lightSE)
|
||||
lightSE.cached_value = -1
|
||||
if(lightSW)
|
||||
lightSW.cached_value = -1
|
||||
|
||||
turf/proc/CheckForOpaqueObjects()
|
||||
has_opaque = opacity
|
||||
if(!opacity)
|
||||
for(var/atom/movable/M in contents)
|
||||
if(M.opacity)
|
||||
has_opaque = 1
|
||||
break
|
||||
|
||||
#undef LIGHTCLAMP
|
||||
73
code/WorkInProgress/Aryn/Lighting/Light.dm
Normal file
73
code/WorkInProgress/Aryn/Lighting/Light.dm
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
|
||||
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/list/lit_turfs
|
||||
light/var/atom/atom
|
||||
|
||||
light/New(atom/atom)
|
||||
src.atom = atom
|
||||
|
||||
light/proc/Reset()
|
||||
//if(atom.lights_verbose) world << "light.Reset()"
|
||||
Off()
|
||||
if(intensity > 0)
|
||||
//if(atom.lights_verbose) world << "Restoring light."
|
||||
for(var/turf/T in view(get_turf(atom),radius+1))
|
||||
if(!T.is_outside)
|
||||
T.AddLight(src)
|
||||
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 = list()
|
||||
|
||||
light/proc/CalculateBrightness(turf/T)
|
||||
var/square = get_square_dist(atom.x,atom.y,atom.z,T.x,T.y,T.z)
|
||||
if(square > (radius+2)*(radius+2)) 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
|
||||
60
code/WorkInProgress/Aryn/Lighting/Lightpoint.dm
Normal file
60
code/WorkInProgress/Aryn/Lighting/Lightpoint.dm
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
|
||||
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()
|
||||
if(cached_value < 0)
|
||||
var
|
||||
valueA = (NW?(NW.lit_value):0)
|
||||
valueB = (NE?(NE.lit_value):0)
|
||||
valueC = (SW?(SW.lit_value):0)
|
||||
valueD = (SE?(SE.lit_value):0)
|
||||
cached_value = max(valueA,valueB,valueC,valueD)
|
||||
return cached_value
|
||||
22
code/WorkInProgress/Aryn/Lighting/Math.dm
Normal file
22
code/WorkInProgress/Aryn/Lighting/Math.dm
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
|
||||
Some math procs used by lighting, including ul's fastroot.
|
||||
|
||||
*/
|
||||
|
||||
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/get_square_dist(Ax,Ay,Az,Bx,By,Bz)
|
||||
var/X = (Ax - Bx)
|
||||
var/Y = (Ay - By)
|
||||
var/Z = (Az - Bz)
|
||||
return (X * X + Y * Y + Z * Z)
|
||||
|
||||
proc/fsqrt(n)
|
||||
if (n > fastroot.len)
|
||||
//world << "Adding [n-fastroot.len] entries to root table."
|
||||
for(var/i = fastroot.len, i <= n, i++)
|
||||
fastroot += round(sqrt(i))
|
||||
return fastroot[n + 1]
|
||||
33
code/WorkInProgress/Aryn/Lighting/Verbs.dm
Normal file
33
code/WorkInProgress/Aryn/Lighting/Verbs.dm
Normal file
@@ -0,0 +1,33 @@
|
||||
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 << "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
|
||||
@@ -49,9 +49,18 @@ turf/c_airblock(turf/other)
|
||||
#endif
|
||||
if(blocks_air)
|
||||
return BLOCKED
|
||||
else
|
||||
var/result = 0
|
||||
for(var/atom/movable/M in contents)
|
||||
result |= M.c_airblock(other)
|
||||
if(result == BLOCKED) return BLOCKED
|
||||
return result
|
||||
|
||||
//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
|
||||
@@ -2,45 +2,52 @@
|
||||
#define CONNECTION_SPACE 4
|
||||
#define CONNECTION_INVALID 8
|
||||
|
||||
/turf/simulated/var/tmp/connection_manager/connections = new
|
||||
/*
|
||||
|
||||
Overview:
|
||||
Connections are made between turfs by air_master.connect(). They represent a single point where two zones converge.
|
||||
|
||||
/connection_manager/var/connection/N
|
||||
/connection_manager/var/connection/S
|
||||
/connection_manager/var/connection/E
|
||||
/connection_manager/var/connection/W
|
||||
Class Vars:
|
||||
A - Always a simulated turf.
|
||||
B - A simulated or unsimulated turf.
|
||||
|
||||
/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
|
||||
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.
|
||||
|
||||
/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
|
||||
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.
|
||||
|
||||
/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()
|
||||
Class Procs:
|
||||
|
||||
/connection_manager/proc/check(connection/c)
|
||||
return c && c.valid()
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/connection/var/turf/simulated/A
|
||||
/connection/var/turf/simulated/B
|
||||
@@ -69,14 +76,12 @@
|
||||
edge.add_connection(src)
|
||||
|
||||
/connection/proc/mark_direct()
|
||||
edge.remove_connection(src)
|
||||
state |= CONNECTION_DIRECT
|
||||
edge.add_connection(src)
|
||||
//world << "Marked direct."
|
||||
|
||||
/connection/proc/mark_indirect()
|
||||
edge.remove_connection(src)
|
||||
state &= ~CONNECTION_DIRECT
|
||||
edge.add_connection(src)
|
||||
//world << "Marked indirect."
|
||||
|
||||
/connection/proc/mark_space()
|
||||
state |= CONNECTION_SPACE
|
||||
@@ -90,6 +95,7 @@
|
||||
/connection/proc/erase()
|
||||
edge.remove_connection(src)
|
||||
state |= CONNECTION_INVALID
|
||||
//world << "Connection Erased: [state]"
|
||||
|
||||
/connection/proc/update()
|
||||
//world << "Updated, \..."
|
||||
|
||||
@@ -1,20 +1,78 @@
|
||||
/*
|
||||
|
||||
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/var/id
|
||||
|
||||
/connection_edge/New()
|
||||
CRASH("Cannot make connection edge without specifications.")
|
||||
|
||||
/connection_edge/proc/add_connection(connection/c)
|
||||
coefficient++
|
||||
//world << "Connection added. Coefficient: [coefficient]"
|
||||
//world << "Connection added: [type] Coefficient: [coefficient]"
|
||||
|
||||
/connection_edge/proc/remove_connection(connection/c)
|
||||
//world << "Connection removed. Coefficient: [coefficient-1]"
|
||||
//world << "Connection removed: [type] Coefficient: [coefficient-1]"
|
||||
coefficient--
|
||||
if(coefficient <= 0)
|
||||
erase()
|
||||
@@ -23,7 +81,7 @@
|
||||
|
||||
/connection_edge/proc/erase()
|
||||
air_master.remove_edge(src)
|
||||
//world << "Erased."
|
||||
//world << "[type] Erased."
|
||||
|
||||
/connection_edge/proc/tick()
|
||||
|
||||
@@ -85,6 +143,9 @@
|
||||
. = ..()
|
||||
|
||||
/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))
|
||||
@@ -115,6 +176,10 @@
|
||||
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
|
||||
@@ -125,23 +190,32 @@
|
||||
A.edges.Add(src)
|
||||
air = B.return_air()
|
||||
//id = 52*A.id
|
||||
//world << "New edge from [A.id] to [B]."
|
||||
//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)
|
||||
ShareSpace(A.air,air,dbg_out)
|
||||
air_master.mark_zone_update(A)
|
||||
|
||||
var/differential = A.air.return_pressure() - air.return_pressure()
|
||||
@@ -259,6 +333,9 @@ proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
|
||||
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
|
||||
@@ -302,10 +379,10 @@ proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
|
||||
|
||||
full_heat_capacity = A.heat_capacity() * size
|
||||
|
||||
oxy_avg = (full_oxy + unsim_oxygen) / (size + share_size)
|
||||
nit_avg = (full_nitro + unsim_nitrogen) / (size + share_size)
|
||||
co2_avg = (full_co2 + unsim_co2) / (size + share_size)
|
||||
plasma_avg = (full_plasma + unsim_plasma) / (size + share_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
|
||||
|
||||
@@ -315,6 +392,10 @@ proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
|
||||
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 )
|
||||
@@ -328,6 +409,8 @@ proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
|
||||
|
||||
A.update_values()
|
||||
|
||||
if(dbg_output) world << "Result: [abs(old_pressure - A.return_pressure())] kPa"
|
||||
|
||||
return abs(old_pressure - A.return_pressure())
|
||||
|
||||
|
||||
|
||||
102
code/ZAS/ConnectionManager.dm
Normal file
102
code/ZAS/ConnectionManager.dm
Normal 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()
|
||||
@@ -2,6 +2,69 @@ 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()
|
||||
@@ -157,6 +220,8 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
|
||||
#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)
|
||||
@@ -171,6 +236,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
|
||||
ASSERT(istype(A))
|
||||
ASSERT(isturf(B))
|
||||
ASSERT(A.zone)
|
||||
ASSERT(!A.zone.invalid)
|
||||
//ASSERT(B.zone)
|
||||
ASSERT(A != B)
|
||||
#endif
|
||||
@@ -190,15 +256,19 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
|
||||
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
|
||||
if(B.connections.get(b_to_a)) return
|
||||
|
||||
|
||||
var/connection/c = new /connection(A,B)
|
||||
|
||||
A.connections.place(c, a_to_b)
|
||||
if(!space) B.connections.place(c, b_to_a)
|
||||
B.connections.place(c, b_to_a)
|
||||
|
||||
if(direct) c.mark_direct()
|
||||
|
||||
@@ -224,9 +294,6 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
|
||||
/datum/controller/air_system/proc/equivalent_pressure(zone/A, zone/B)
|
||||
return A.air.compare(B.air)
|
||||
|
||||
/datum/controller/air_system/proc/active_zones()
|
||||
return active_zones
|
||||
|
||||
/datum/controller/air_system/proc/get_edge(zone/A, zone/B)
|
||||
|
||||
if(istype(B))
|
||||
|
||||
@@ -7,6 +7,8 @@ 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")
|
||||
|
||||
/connection_edge/var/dbg_out = 0
|
||||
|
||||
/turf/var/tmp/dbg_img
|
||||
/turf/proc/dbg(image/img, d = 0)
|
||||
if(d > 0) img.dir = d
|
||||
@@ -14,7 +16,5 @@ var/image/mark = image('icons/Testing/Zone.dmi', icon_state = "mark")
|
||||
overlays += img
|
||||
dbg_img = img
|
||||
|
||||
/turf/simulated/var/verbose = 0
|
||||
/turf/simulated/verb/Verbose()
|
||||
set src in world
|
||||
verbose = !verbose
|
||||
proc/soft_assert(thing,fail)
|
||||
if(!thing) message_admins(fail)
|
||||
@@ -17,6 +17,9 @@ client/proc/Zone_Info(turf/T as null|turf)
|
||||
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)
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
//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)
|
||||
@@ -28,6 +32,11 @@
|
||||
//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
|
||||
@@ -48,7 +57,7 @@
|
||||
#endif
|
||||
if(zone)
|
||||
var/zone/z = zone
|
||||
if(s_block & ZONE_BLOCKED)
|
||||
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()
|
||||
@@ -59,7 +68,11 @@
|
||||
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)
|
||||
@@ -157,9 +170,7 @@
|
||||
air_master.connect(src, T)
|
||||
|
||||
/turf/proc/post_update_air_properties()
|
||||
|
||||
/turf/simulated/post_update_air_properties()
|
||||
connections.update_all()
|
||||
if(connections) connections.update_all()
|
||||
|
||||
/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
|
||||
del(giver)
|
||||
|
||||
@@ -1,3 +1,44 @@
|
||||
/*
|
||||
|
||||
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
|
||||
@@ -33,9 +74,11 @@
|
||||
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
|
||||
@@ -64,6 +107,7 @@
|
||||
#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)
|
||||
@@ -84,12 +128,6 @@
|
||||
for(var/turf/simulated/T in contents)
|
||||
T.set_graphic(air.graphic)
|
||||
|
||||
/zone/proc/remove_connection(connection/c)
|
||||
return
|
||||
|
||||
/zone/proc/add_connection(connection/c)
|
||||
return
|
||||
|
||||
/zone/proc/dbg_data(mob/M)
|
||||
M << name
|
||||
M << "O2: [air.oxygen] N2: [air.nitrogen] CO2: [air.carbon_dioxide] P: [air.toxins]"
|
||||
@@ -107,6 +145,7 @@
|
||||
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)"
|
||||
|
||||
@@ -19,9 +19,16 @@ 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
|
||||
|
||||
@@ -161,8 +161,6 @@
|
||||
// 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.
|
||||
|
||||
@@ -174,33 +172,18 @@
|
||||
|
||||
var/list/range = hear(R, T)
|
||||
|
||||
for(var/mob/M in range)
|
||||
hear += M
|
||||
|
||||
var/list/objects = list()
|
||||
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/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
|
||||
if(isobj(A) || ismob(A))
|
||||
hear |= recursive_mob_check(A, hear, 3, 1, 0, 1)
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ 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/joblist = list() //list of all jobstypes, minus borg and AI
|
||||
|
||||
//Languages/species/whitelist.
|
||||
var/global/list/all_species[0]
|
||||
@@ -85,6 +86,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
|
||||
|
||||
@@ -200,7 +200,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.
|
||||
@@ -270,7 +270,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)
|
||||
@@ -299,7 +299,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*")
|
||||
|
||||
@@ -1267,6 +1267,7 @@ proc/is_hot(obj/item/W as obj)
|
||||
|
||||
//Is this even used for anything besides balloons? Yes I took out the W:lit stuff because : really shouldnt be used.
|
||||
/proc/is_sharp(obj/item/W as obj) // For the record, WHAT THE HELL IS THIS METHOD OF DOING IT?
|
||||
if(!W) return 0
|
||||
if(W.sharp) return 1
|
||||
return ( \
|
||||
W.sharp || \
|
||||
@@ -1374,4 +1375,4 @@ var/list/WALLITEMS = list(
|
||||
return 0
|
||||
|
||||
/proc/format_text(text)
|
||||
return replacetext(replacetext(text,"\proper ",""),"\improper ","")
|
||||
return replacetext(replacetext(text,"\proper ",""),"\improper ","")
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
build_click(src, client.buildmode, params, A)
|
||||
return
|
||||
|
||||
if(control_disabled || stat)
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
@@ -46,7 +49,8 @@
|
||||
CtrlClickOn(A)
|
||||
return
|
||||
|
||||
if(control_disabled || stat || world.time <= next_move) return
|
||||
if(world.time <= next_move)
|
||||
return
|
||||
next_move = world.time + 9
|
||||
|
||||
/*
|
||||
@@ -123,4 +127,4 @@
|
||||
else
|
||||
// disable/6 is not in Topic; disable/5 disables both temporary and permenant shock
|
||||
Topic("aiDisable=5", list("aiDisable"="5"), 1)
|
||||
return
|
||||
return
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
user.attack_log += "\[[time_stamp()]\]<font color='red'> Attacked [M.name] ([M.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])</font>"
|
||||
M.attack_log += "\[[time_stamp()]\]<font color='orange'> Attacked by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])</font>"
|
||||
log_attack("<font color='red'>[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])</font>" )
|
||||
msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" )
|
||||
|
||||
//spawn(1800) // this wont work right
|
||||
// M.lastattacker = null
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -77,6 +77,7 @@ datum/controller/game_controller/proc/setup()
|
||||
if(ticker)
|
||||
ticker.pregame()
|
||||
|
||||
new/datum/controller/lighting()
|
||||
lighting_controller.Initialize()
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -357,7 +357,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
|
||||
|
||||
|
||||
@@ -390,6 +390,15 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
||||
containername = "Glass sheets crate"
|
||||
group = "Engineering"
|
||||
|
||||
/datum/supply_packs/wood50
|
||||
name = "50 Wooden Planks"
|
||||
contains = list(/obj/item/stack/sheet/wood)
|
||||
amount = 50
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Wooden planks crate"
|
||||
group = "Engineering"
|
||||
|
||||
/datum/supply_packs/electrical
|
||||
name = "Electrical maintenance crate"
|
||||
contains = list(/obj/item/weapon/storage/toolbox/electrical,
|
||||
|
||||
@@ -24,20 +24,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)
|
||||
|
||||
@@ -775,13 +775,9 @@ var/list/sacrificed = list()
|
||||
return
|
||||
cultist.buckled = null
|
||||
if (cultist.handcuffed)
|
||||
cultist.handcuffed.loc = cultist.loc
|
||||
cultist.handcuffed = null
|
||||
cultist.update_inv_handcuffed()
|
||||
cultist.drop_from_inventory(cultist.handcuffed)
|
||||
if (cultist.legcuffed)
|
||||
cultist.legcuffed.loc = cultist.loc
|
||||
cultist.legcuffed = null
|
||||
cultist.update_inv_legcuffed()
|
||||
cultist.drop_from_inventory(cultist.legcuffed)
|
||||
if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle))
|
||||
cultist.u_equip(cultist.wear_mask)
|
||||
if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded)
|
||||
|
||||
@@ -148,7 +148,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
|
||||
supply_shuttle.process() //Start the supply shuttle regenerating points -- TLE
|
||||
master_controller.process() //Start master_controller.process()
|
||||
lighting_controller.process() //Start processing DynamicAreaLighting updates
|
||||
//lighting_controller.process() //Start processing DynamicAreaLighting updates
|
||||
|
||||
for(var/obj/multiz/ladder/L in world) L.connect() //Lazy hackfix for ladders. TODO: move this to an actual controller. ~ Z
|
||||
|
||||
|
||||
@@ -213,11 +213,11 @@
|
||||
if(prob(prob_right_job))
|
||||
if(correct_person)
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
changeling_job = pick(get_all_jobs())
|
||||
changeling_job = pick(joblist)
|
||||
else
|
||||
changeling_job = correct_person:assigned_role
|
||||
else
|
||||
changeling_job = pick(get_all_jobs())
|
||||
changeling_job = pick(joblist)
|
||||
if(prob(prob_right_dude) && ticker.mode == "changeling")
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
changeling_name = correct_person:current
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
tried_to_add_revheads = world.time+50
|
||||
var/active_revs = 0
|
||||
for(var/datum/mind/rev_mind in head_revolutionaries)
|
||||
if(rev_mind.current.client && rev_mind.current.client.inactivity <= 10*60*20) // 20 minutes inactivity are OK
|
||||
if(rev_mind.current && rev_mind.current.client && rev_mind.current.client.inactivity <= 10*60*20) // 20 minutes inactivity are OK
|
||||
active_revs++
|
||||
|
||||
if(active_revs == 0)
|
||||
|
||||
@@ -440,10 +440,10 @@
|
||||
rank = src:rank
|
||||
assignment = src:assignment
|
||||
|
||||
if( rank in get_all_jobs() )
|
||||
if( rank in joblist )
|
||||
return rank
|
||||
|
||||
if( assignment in get_all_jobs() )
|
||||
if( assignment in joblist )
|
||||
return assignment
|
||||
|
||||
return "Unknown"
|
||||
@@ -496,7 +496,7 @@ proc/FindNameFromID(var/mob/living/carbon/human/H)
|
||||
return ID.registered_name
|
||||
|
||||
proc/get_all_job_icons() //For all existing HUD icons
|
||||
return get_all_jobs() + list("Prisoner")
|
||||
return joblist + list("Prisoner")
|
||||
|
||||
/obj/proc/GetJobName() //Used in secHUD icon generation
|
||||
if (!istype(src, /obj/item/device/pda) && !istype(src,/obj/item/weapon/card/id))
|
||||
|
||||
@@ -183,24 +183,25 @@
|
||||
|
||||
var/datum/gas_mixture/gas
|
||||
gas = location.remove_air(0.25*environment.total_moles)
|
||||
var/heat_capacity = gas.heat_capacity()
|
||||
var/energy_used = min( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE)
|
||||
if(gas)
|
||||
var/heat_capacity = gas.heat_capacity()
|
||||
var/energy_used = min( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE)
|
||||
|
||||
//Use power. Assuming that each power unit represents 1000 watts....
|
||||
use_power(energy_used/1000, ENVIRON)
|
||||
//Use power. Assuming that each power unit represents 1000 watts....
|
||||
use_power(energy_used/1000, ENVIRON)
|
||||
|
||||
//We need to cool ourselves.
|
||||
if(environment.temperature > target_temperature)
|
||||
gas.temperature -= energy_used/heat_capacity
|
||||
else
|
||||
gas.temperature += energy_used/heat_capacity
|
||||
//We need to cool ourselves.
|
||||
if(environment.temperature > target_temperature)
|
||||
gas.temperature -= energy_used/heat_capacity
|
||||
else
|
||||
gas.temperature += energy_used/heat_capacity
|
||||
|
||||
environment.merge(gas)
|
||||
environment.merge(gas)
|
||||
|
||||
if(abs(environment.temperature - target_temperature) <= 0.5)
|
||||
regulating_temperature = 0
|
||||
visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
|
||||
"You hear a click as a faint electronic humming stops.")
|
||||
if(abs(environment.temperature - target_temperature) <= 0.5)
|
||||
regulating_temperature = 0
|
||||
visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
|
||||
"You hear a click as a faint electronic humming stops.")
|
||||
|
||||
var/old_level = danger_level
|
||||
danger_level = overall_danger_level()
|
||||
@@ -1683,4 +1684,4 @@ Code shamelessly copied from apc_frame
|
||||
else
|
||||
usr << browse(null, "window=partyalarm")
|
||||
return
|
||||
return
|
||||
return
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
header += "<hr>"
|
||||
|
||||
var/jobs_all = ""
|
||||
var/list/alljobs = (istype(src,/obj/machinery/computer/card/centcom)? get_all_centcom_jobs() : get_all_jobs()) + "Custom"
|
||||
var/list/alljobs = (istype(src,/obj/machinery/computer/card/centcom)? get_all_centcom_jobs() : joblist) + "Custom"
|
||||
for(var/job in alljobs)
|
||||
jobs_all += "<a href='?src=\ref[src];choice=assign;assign_target=[job]'>[replacetext(job, " ", " ")]</a> " //make sure there isn't a line break in the middle of a job
|
||||
|
||||
|
||||
@@ -501,7 +501,7 @@ What a mess.*/
|
||||
if ((istype(active1, /datum/data/record) && L.Find(rank)))
|
||||
temp = "<h5>Rank:</h5>"
|
||||
temp += "<ul>"
|
||||
for(var/rank in get_all_jobs())
|
||||
for(var/rank in joblist)
|
||||
temp += "<li><a href='?src=\ref[src];choice=Change Rank;rank=[rank]'>[rank]</a></li>"
|
||||
temp += "</ul>"
|
||||
else
|
||||
@@ -520,7 +520,7 @@ What a mess.*/
|
||||
if ("Change Rank")
|
||||
if (active1)
|
||||
active1.fields["rank"] = href_list["rank"]
|
||||
if(href_list["rank"] in get_all_jobs())
|
||||
if(href_list["rank"] in joblist)
|
||||
active1.fields["real_rank"] = href_list["real_rank"]
|
||||
|
||||
if ("Change Criminal Status")
|
||||
|
||||
@@ -354,7 +354,7 @@ What a mess.*/
|
||||
if ((istype(active1, /datum/data/record) && L.Find(rank)))
|
||||
temp = "<h5>Rank:</h5>"
|
||||
temp += "<ul>"
|
||||
for(var/rank in get_all_jobs())
|
||||
for(var/rank in joblist)
|
||||
temp += "<li><a href='?src=\ref[src];choice=Change Rank;rank=[rank]'>[rank]</a></li>"
|
||||
temp += "</ul>"
|
||||
else
|
||||
@@ -373,7 +373,7 @@ What a mess.*/
|
||||
if ("Change Rank")
|
||||
if (active1)
|
||||
active1.fields["rank"] = href_list["rank"]
|
||||
if(href_list["rank"] in get_all_jobs())
|
||||
if(href_list["rank"] in joblist)
|
||||
active1.fields["real_rank"] = href_list["real_rank"]
|
||||
|
||||
if ("Delete Record (ALL) Execute")
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
if(src.occupant.client) // Gibbed a cow with a client in it? log that shit
|
||||
src.occupant.attack_log += "\[[time_stamp()]\] Was gibbed by <b>[user]/[user.ckey]</b>"
|
||||
user.attack_log += "\[[time_stamp()]\] Gibbed <b>[src.occupant]/[src.occupant.ckey]</b>"
|
||||
log_attack("\[[time_stamp()]\] <b>[user]/[user.ckey]</b> gibbed <b>[src.occupant]/[src.occupant.ckey]</b>")
|
||||
msg_admin_attack("\[[time_stamp()]\] <b>[key_name(user)]</b> gibbed <b>[key_name(src.occupant)]</b>")
|
||||
|
||||
src.occupant.death(1)
|
||||
src.occupant.ghostize()
|
||||
|
||||
252
code/game/objects/effects/chemsmoke.dm
Normal file
252
code/game/objects/effects/chemsmoke.dm
Normal file
@@ -0,0 +1,252 @@
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/effect/smoke/chem
|
||||
icon = 'icons/effects/chemsmoke.dmi'
|
||||
opacity = 0
|
||||
time_to_live = 300
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSGLASS //PASSGLASS is fine here, it's just so the visual effect can "flow" around glass
|
||||
|
||||
/obj/effect/effect/smoke/chem/New()
|
||||
..()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
reagents = R
|
||||
R.my_atom = src
|
||||
return
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem
|
||||
smoke_type = /obj/effect/effect/smoke/chem
|
||||
var/obj/chemholder
|
||||
var/range
|
||||
var/list/targetTurfs
|
||||
var/list/wallList
|
||||
var/density
|
||||
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem/New()
|
||||
..()
|
||||
chemholder = new/obj()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
chemholder.reagents = R
|
||||
R.my_atom = chemholder
|
||||
|
||||
//------------------------------------------
|
||||
//Sets up the chem smoke effect
|
||||
//
|
||||
// Calculates the max range smoke can travel, then gets all turfs in that view range.
|
||||
// Culls the selected turfs to a (roughly) circle shape, then calls smokeFlow() to make
|
||||
// sure the smoke can actually path to the turfs. This culls any turfs it can't reach.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/set_up(var/datum/reagents/carry = null, n = 10, c = 0, loca, direct)
|
||||
range = n * 0.3
|
||||
cardinals = c
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
if(!location)
|
||||
return
|
||||
|
||||
targetTurfs = new()
|
||||
|
||||
//build affected area list
|
||||
for(var/turf/T in view(range, location))
|
||||
//cull turfs to circle
|
||||
if(cheap_pythag(T.x - location.x, T.y - location.y) <= range)
|
||||
targetTurfs += T
|
||||
|
||||
//make secondary list for reagents that affect walls
|
||||
if(chemholder.reagents.has_reagent("thermite") || chemholder.reagents.has_reagent("plantbgone"))
|
||||
wallList = new()
|
||||
|
||||
//pathing check
|
||||
smokeFlow(location, targetTurfs, wallList)
|
||||
|
||||
//set the density of the cloud - for diluting reagents
|
||||
density = max(1, targetTurfs.len / 4) //clamp the cloud density minimum to 1 so it cant multiply the reagents
|
||||
|
||||
//Admin messaging
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
var/area/A = get_area(location)
|
||||
|
||||
var/where = "[A.name] | [location.x], [location.y]"
|
||||
var/whereLink = "<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
//Runs the chem smoke effect
|
||||
//
|
||||
// Spawns damage over time loop for each reagent held in the cloud.
|
||||
// Applies reagents to walls that affect walls (only thermite and plant-b-gone at the moment).
|
||||
// Also calculates target locations to spawn the visual smoke effect on, so the whole area
|
||||
// is covered fairly evenly.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/start()
|
||||
|
||||
if(!location) //kill grenade if it somehow ends up in nullspace
|
||||
return
|
||||
|
||||
//reagent application - only run if there are extra reagents in the smoke
|
||||
if(chemholder.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in chemholder.reagents.reagent_list)
|
||||
var/proba = 100
|
||||
var/runs = 5
|
||||
|
||||
//dilute the reagents according to cloud density
|
||||
R.volume /= density
|
||||
chemholder.reagents.update_total()
|
||||
|
||||
//apply wall affecting reagents to walls
|
||||
if(R.id in list("thermite", "plantbgone"))
|
||||
for(var/turf/T in wallList)
|
||||
R.reaction_turf(T, R.volume)
|
||||
|
||||
//reagents that should be applied to turfs in a random pattern
|
||||
if(R.id == "carbon")
|
||||
proba = 75
|
||||
else if(R.id in list("blood", "radium", "uranium"))
|
||||
proba = 25
|
||||
|
||||
spawn(0)
|
||||
for(var/i = 0, i < runs, i++)
|
||||
for(var/turf/T in targetTurfs)
|
||||
if(prob(proba))
|
||||
R.reaction_turf(T, R.volume)
|
||||
for(var/atom/A in T.contents)
|
||||
if(istype(A, /obj/effect/effect/smoke/chem)) //skip the item if it is chem smoke
|
||||
continue
|
||||
else if(istype(A, /mob))
|
||||
var/dist = cheap_pythag(T.x - location.x, T.y - location.y)
|
||||
if(!dist)
|
||||
dist = 1
|
||||
R.reaction_mob(A, volume = R.volume / dist)
|
||||
else if(istype(A, /obj))
|
||||
R.reaction_obj(A, R.volume)
|
||||
sleep(30)
|
||||
|
||||
|
||||
//build smoke icon
|
||||
var/color = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
var/icon/I
|
||||
if(color)
|
||||
I = icon('icons/effects/chemsmoke.dmi')
|
||||
I += color
|
||||
else
|
||||
I = icon('icons/effects/96x96.dmi', "smoke")
|
||||
|
||||
|
||||
//distance between each smoke cloud
|
||||
var/const/arcLength = 2.3559
|
||||
|
||||
|
||||
//calculate positions for smoke coverage - then spawn smoke
|
||||
for(var/i = 0, i < range, i++)
|
||||
var/radius = i * 1.5
|
||||
if(!radius)
|
||||
spawn(0)
|
||||
spawnSmoke(location, I, 1)
|
||||
continue
|
||||
|
||||
var/offset = 0
|
||||
var/points = round((radius * 2 * PI) / arcLength)
|
||||
var/angle = round(ToDegrees(arcLength / radius), 1)
|
||||
|
||||
if(!IsInteger(radius))
|
||||
offset = 45 //degrees
|
||||
|
||||
for(var/j = 0, j < points, j++)
|
||||
var/a = (angle * j) + offset
|
||||
var/x = round(radius * cos(a) + location.x, 1)
|
||||
var/y = round(radius * sin(a) + location.y, 1)
|
||||
var/turf/T = locate(x,y,location.z)
|
||||
if(!T)
|
||||
continue
|
||||
if(T in targetTurfs)
|
||||
spawn(0)
|
||||
spawnSmoke(T, I, range)
|
||||
|
||||
//------------------------------------------
|
||||
// Randomizes and spawns the smoke effect.
|
||||
// Also handles deleting the smoke once the effect is finished.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/spawnSmoke(var/turf/T, var/icon/I, var/dist = 1)
|
||||
var/obj/effect/effect/smoke/chem/smoke = new(location)
|
||||
if(chemholder.reagents.reagent_list.len)
|
||||
chemholder.reagents.copy_to(smoke, chemholder.reagents.total_volume / dist, safety = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents
|
||||
smoke.icon = I
|
||||
smoke.layer = 6
|
||||
smoke.dir = pick(cardinal)
|
||||
smoke.pixel_x = -32 + rand(-8,8)
|
||||
smoke.pixel_y = -32 + rand(-8,8)
|
||||
walk_to(smoke, T)
|
||||
smoke.opacity = 1 //switching opacity on after the smoke has spawned, and then
|
||||
sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner
|
||||
smoke.opacity = 0 // lighting and view range updates
|
||||
fadeOut(smoke)
|
||||
smoke.delete()
|
||||
|
||||
//------------------------------------------
|
||||
// Fades out the smoke smoothly using it's alpha variable.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/fadeOut(var/atom/A, var/frames = 16)
|
||||
var/step = A.alpha / frames
|
||||
for(var/i = 0, i < frames, i++)
|
||||
A.alpha -= step
|
||||
sleep(world.tick_lag)
|
||||
return
|
||||
|
||||
//------------------------------------------
|
||||
// Smoke pathfinder. Uses a flood fill method based on zones to
|
||||
// quickly check what turfs the smoke (airflow) can actually reach.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/smokeFlow()
|
||||
|
||||
var/list/pending = new()
|
||||
var/list/complete = new()
|
||||
|
||||
pending += location
|
||||
|
||||
while(pending.len)
|
||||
for(var/turf/simulated/current in pending)
|
||||
for(var/D in cardinal)
|
||||
var/turf/simulated/target = get_step(current, D)
|
||||
|
||||
if(wallList)
|
||||
if(istype(target, /turf/simulated/wall))
|
||||
if(!(target in wallList))
|
||||
wallList += target
|
||||
continue
|
||||
if(!target.zone)
|
||||
continue
|
||||
if(target in pending)
|
||||
continue
|
||||
if(target in complete)
|
||||
continue
|
||||
if(!(target in targetTurfs))
|
||||
continue
|
||||
|
||||
pending += target
|
||||
|
||||
pending -= current
|
||||
complete += current
|
||||
|
||||
targetTurfs = complete
|
||||
|
||||
return
|
||||
@@ -201,7 +201,8 @@ steam.start() -- spawns the effect
|
||||
sleep(5)
|
||||
step(sparks,direction)
|
||||
spawn(20)
|
||||
sparks.delete()
|
||||
if(sparks)
|
||||
sparks.delete()
|
||||
src.total_sparks--
|
||||
|
||||
|
||||
@@ -379,121 +380,6 @@ steam.start() -- spawns the effect
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/mustard
|
||||
smoke_type = /obj/effect/effect/smoke/mustard
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/effect/smoke/chem
|
||||
icon = 'icons/effects/chemsmoke.dmi'
|
||||
|
||||
/obj/effect/effect/smoke/chem/New()
|
||||
..()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
reagents = R
|
||||
R.my_atom = src
|
||||
return
|
||||
|
||||
/obj/effect/effect/smoke/chem/proc/applyReagents()
|
||||
if(reagents.reagent_list.len)
|
||||
for(var/atom/A in view(1, src))
|
||||
if(!istype(A, src.type))
|
||||
if(reagents.has_reagent("radium")||reagents.has_reagent("uranium")||reagents.has_reagent("carbon")||reagents.has_reagent("thermite"))//Prevents unholy radium spam by reducing the number of 'greenglows' down to something reasonable -Sieve
|
||||
if(prob(5))
|
||||
reagents.reaction(A)
|
||||
else
|
||||
reagents.reaction(A)
|
||||
|
||||
/obj/effect/effect/smoke/chem/affect(mob/living/carbon/M as mob )
|
||||
reagents.reaction(M)
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem
|
||||
smoke_type = /obj/effect/effect/smoke/chem
|
||||
var/obj/chemholder
|
||||
|
||||
New()
|
||||
..()
|
||||
chemholder = new/obj()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
chemholder.reagents = R
|
||||
R.my_atom = chemholder
|
||||
|
||||
set_up(var/datum/reagents/carry = null, n = 5, c = 0, loca, direct)
|
||||
if(n > 20)
|
||||
n = 20
|
||||
number = n
|
||||
cardinals = c
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
if(direct)
|
||||
direction = direct
|
||||
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
var/area/A = get_area(location)
|
||||
|
||||
var/where = "[A.name] | [location.x], [location.y]"
|
||||
var/whereLink = "<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
start()
|
||||
var/i = 0
|
||||
|
||||
var/color = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
|
||||
for(i=0, i<src.number, i++)
|
||||
if(src.total_smoke > 20)
|
||||
return
|
||||
spawn(0)
|
||||
if(holder)
|
||||
src.location = get_turf(holder)
|
||||
var/obj/effect/effect/smoke/chem/smoke = new /obj/effect/effect/smoke/chem(src.location)
|
||||
src.total_smoke++
|
||||
var/direction = src.direction
|
||||
if(!direction)
|
||||
if(src.cardinals)
|
||||
direction = pick(cardinal)
|
||||
else
|
||||
direction = pick(alldirs)
|
||||
|
||||
if(chemholder.reagents.total_volume != 1) // can't split 1 very well
|
||||
chemholder.reagents.copy_to(smoke, chemholder.reagents.total_volume / number, safety = 1) // copy reagents to each smoke, divide evenly
|
||||
|
||||
if(color)
|
||||
smoke.icon += color // give the smoke color, if it has any to begin with
|
||||
else
|
||||
// if no color, just use the old smoke icon
|
||||
smoke.icon = 'icons/effects/96x96.dmi'
|
||||
smoke.icon_state = "smoke"
|
||||
|
||||
for(i=0, i<pick(0,1,1,1,2,2,2,3), i++)
|
||||
sleep(10)
|
||||
step(smoke,direction)
|
||||
|
||||
//apply the reagents to the environment after the smoke has stopped moving - to reduce reagent spam
|
||||
for(i=0, i<2 + pick(0,1,), i++)
|
||||
smoke.applyReagents()
|
||||
sleep(20)
|
||||
|
||||
sleep(60)
|
||||
smoke.delete()
|
||||
src.total_smoke--
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
@@ -133,9 +133,6 @@ move an amendment</a> to the drawing.</p>
|
||||
move_turfs_to_area(turfs, A)
|
||||
|
||||
A.always_unpowered = 0
|
||||
for(var/turf/T in A.contents)
|
||||
T.lighting_changed = 1
|
||||
lighting_controller.changed_turfs += T
|
||||
|
||||
spawn(5)
|
||||
//ma = A.master ? "[A.master]" : "(null)"
|
||||
|
||||
@@ -482,7 +482,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
|
||||
return 0
|
||||
if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that.
|
||||
U.unset_machine()
|
||||
ui.close()
|
||||
if(ui)
|
||||
ui.close()
|
||||
return 0
|
||||
|
||||
add_fingerprint(U)
|
||||
|
||||
@@ -21,6 +21,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
icon = 'icons/obj/cigarettes.dmi'
|
||||
icon_state = "match_unlit"
|
||||
var/lit = 0
|
||||
var/burnt = 0
|
||||
var/smoketime = 5
|
||||
w_class = 1.0
|
||||
origin_tech = "materials=1"
|
||||
@@ -30,24 +31,27 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
var/turf/location = get_turf(src)
|
||||
smoketime--
|
||||
if(smoketime < 1)
|
||||
icon_state = "match_burnt"
|
||||
lit = -1
|
||||
processing_objects.Remove(src)
|
||||
burn_out()
|
||||
return
|
||||
if(location)
|
||||
location.hotspot_expose(700, 5)
|
||||
return
|
||||
|
||||
/obj/item/weapon/match/dropped(mob/user as mob)
|
||||
if(lit == 1)
|
||||
lit = -1
|
||||
damtype = "brute"
|
||||
icon_state = "match_burnt"
|
||||
item_state = "cigoff"
|
||||
name = "burnt match"
|
||||
desc = "A match. This one has seen better days."
|
||||
if(lit)
|
||||
burn_out()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/match/proc/burn_out()
|
||||
lit = 0
|
||||
burnt = 1
|
||||
damtype = "brute"
|
||||
icon_state = "match_burnt"
|
||||
item_state = "cigoff"
|
||||
name = "burnt match"
|
||||
desc = "A match. This one has seen better days."
|
||||
processing_objects.Remove(src)
|
||||
|
||||
//////////////////
|
||||
//FINE SMOKABLES//
|
||||
//////////////////
|
||||
@@ -419,4 +423,4 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
if(lit)
|
||||
user.SetLuminosity(user.luminosity-2)
|
||||
SetLuminosity(2)
|
||||
return
|
||||
return
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
|
||||
H.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been wrapped with [src.name] by [user.name] ([user.ckey])</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to wrap [H.name] ([H.ckey])</font>")
|
||||
log_attack("[user.name] ([user.ckey]) used the [src.name] to wrap [H.name] ([H.ckey])")
|
||||
msg_admin_attack("[key_name(user)] used [src] to wrap [key_name(H)]")
|
||||
|
||||
else
|
||||
user << "\blue You need more paper."
|
||||
|
||||
@@ -161,6 +161,10 @@
|
||||
if( A == src ) continue
|
||||
src.reagents.reaction(A, 1, 10)
|
||||
|
||||
if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand
|
||||
var/mob/living/carbon/C = loc
|
||||
C.drop_from_inventory(src)
|
||||
C.throw_mode_off()
|
||||
|
||||
invisibility = INVISIBILITY_MAXIMUM //Why am i doing this?
|
||||
spawn(50) //To make sure all reagents can work
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
C.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been handcuffed (attempt) by [user.name] ([user.ckey])</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Attempted to handcuff [C.name] ([C.ckey])</font>")
|
||||
log_attack("[user.name] ([user.ckey]) Attempted to handcuff [C.name] ([C.ckey])")
|
||||
msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(C)]")
|
||||
|
||||
var/obj/effect/equip_e/human/O = new /obj/effect/equip_e/human( )
|
||||
O.source = user
|
||||
@@ -92,6 +92,31 @@
|
||||
return
|
||||
return
|
||||
|
||||
var/last_chew = 0
|
||||
/mob/living/carbon/human/RestrainedClickOn(var/atom/A)
|
||||
if (A != src) return ..()
|
||||
if (last_chew + 26 > world.time) return
|
||||
|
||||
var/mob/living/carbon/human/H = A
|
||||
if (!H.handcuffed) return
|
||||
if (H.a_intent != "hurt") return
|
||||
if (H.zone_sel.selecting != "mouth") return
|
||||
if (H.wear_mask) return
|
||||
if (istype(H.wear_suit, /obj/item/clothing/suit/straight_jacket)) return
|
||||
|
||||
var/datum/organ/external/O = H.organs_by_name[H.hand?"l_hand":"r_hand"]
|
||||
if (!O) return
|
||||
|
||||
var/s = "\red [H.name] chews on \his [O.display_name]!"
|
||||
H.visible_message(s, "\red You chew on your [O.display_name]!")
|
||||
H.attack_log += text("\[[time_stamp()]\] <font color='red'>[s] ([H.ckey])</font>")
|
||||
log_attack("[s] ([H.ckey])")
|
||||
|
||||
if(O.take_damage(3,0,1,"teeth marks"))
|
||||
H:UpdateDamageIcon()
|
||||
|
||||
last_chew = world.time
|
||||
|
||||
/obj/item/weapon/handcuffs/cable
|
||||
name = "cable restraints"
|
||||
desc = "Looks like some cables tied together. Could be used to tie something up."
|
||||
@@ -123,4 +148,4 @@
|
||||
color = "#FFFFFF"
|
||||
|
||||
/obj/item/weapon/handcuffs/cyborg
|
||||
dispenser = 1
|
||||
dispenser = 1
|
||||
|
||||
@@ -173,7 +173,6 @@
|
||||
desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage."
|
||||
icon_state = "swatbelt"
|
||||
item_state = "swatbelt"
|
||||
var/obj/item/weapon/gun/holstered = null
|
||||
storage_slots = 9
|
||||
max_w_class = 3
|
||||
max_combined_w_class = 21
|
||||
@@ -195,40 +194,5 @@
|
||||
"/obj/item/device/flashlight",
|
||||
"/obj/item/device/pda",
|
||||
"/obj/item/device/radio/headset",
|
||||
"/obj/item/weapon/melee",
|
||||
"/obj/item/taperoll/police",
|
||||
"/obj/item/weapon/gun/energy/taser"
|
||||
)
|
||||
|
||||
|
||||
/obj/item/weapon/storage/belt/security/tactical/verb/holster()
|
||||
set name = "Holster"
|
||||
set category = "Object"
|
||||
set src in usr
|
||||
if(!istype(usr, /mob/living)) return
|
||||
if(usr.stat) return
|
||||
|
||||
if(!holstered)
|
||||
if(!istype(usr.get_active_hand(), /obj/item/weapon/gun))
|
||||
usr << "\blue You need your gun equiped to holster it."
|
||||
return
|
||||
var/obj/item/weapon/gun/W = usr.get_active_hand()
|
||||
if (!W.isHandgun())
|
||||
usr << "\red This gun won't fit in \the belt!"
|
||||
return
|
||||
holstered = usr.get_active_hand()
|
||||
usr.drop_item()
|
||||
holstered.loc = src
|
||||
usr.visible_message("\blue \The [usr] holsters \the [holstered].", "You holster \the [holstered].")
|
||||
else
|
||||
if(istype(usr.get_active_hand(),/obj) && istype(usr.get_inactive_hand(),/obj))
|
||||
usr << "\red You need an empty hand to draw the gun!"
|
||||
else
|
||||
if(usr.a_intent == "hurt")
|
||||
usr.visible_message("\red \The [usr] draws \the [holstered], ready to shoot!", \
|
||||
"\red You draw \the [holstered], ready to shoot!")
|
||||
else
|
||||
usr.visible_message("\blue \The [usr] draws \the [holstered], pointing it at the ground.", \
|
||||
"\blue You draw \the [holstered], pointing it at the ground.")
|
||||
usr.put_in_hands(holstered)
|
||||
holstered = null
|
||||
"/obj/item/weapon/melee"
|
||||
)
|
||||
@@ -418,6 +418,7 @@
|
||||
w_class = 1
|
||||
flags = TABLEPASS
|
||||
slot_flags = SLOT_BELT
|
||||
can_hold = list("/obj/item/weapon/match")
|
||||
|
||||
New()
|
||||
..()
|
||||
@@ -425,8 +426,9 @@
|
||||
new /obj/item/weapon/match(src)
|
||||
|
||||
attackby(obj/item/weapon/match/W as obj, mob/user as mob)
|
||||
if(istype(W, /obj/item/weapon/match) && W.lit == 0)
|
||||
if(istype(W) && !W.lit && !W.burnt)
|
||||
W.lit = 1
|
||||
W.damtype = "burn"
|
||||
W.icon_state = "match_lit"
|
||||
processing_objects.Add(W)
|
||||
W.update_icon()
|
||||
|
||||
@@ -173,7 +173,6 @@
|
||||
|
||||
/obj/item/weapon/storage/fancy/cigarettes/update_icon()
|
||||
icon_state = "[initial(icon_state)][contents.len]"
|
||||
desc = "There are [contents.len] cig\s left!"
|
||||
return
|
||||
|
||||
/obj/item/weapon/storage/fancy/cigarettes/remove_from_storage(obj/item/W as obj, atom/new_location)
|
||||
@@ -253,5 +252,4 @@
|
||||
|
||||
/obj/item/weapon/storage/lockbox/vials/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
update_icon()
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
user.attack_log += "\[[time_stamp()]\]<font color='red'> Stunned [H.name] ([H.ckey]) with [src.name]</font>"
|
||||
H.attack_log += "\[[time_stamp()]\]<font color='orange'> Stunned by [user.name] ([user.ckey]) with [src.name]</font>"
|
||||
log_attack("[user.name] ([user.ckey]) stunned [H.name] ([H.ckey]) with [src.name]")
|
||||
msg_admin_attack("[key_name(user)] stunned [key_name(H)] with [src.name]")
|
||||
|
||||
playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
if(charges < 1)
|
||||
@@ -115,7 +115,7 @@
|
||||
H.visible_message("<span class='danger'>[src], thrown by [foundmob.name], strikes [H] and stuns them!</span>")
|
||||
|
||||
H.attack_log += "\[[time_stamp()]\]<font color='orange'> Stunned by thrown [src.name] last touched by ([src.fingerprintslast])</font>"
|
||||
log_attack("Flying [src.name], last touched by ([src.fingerprintslast]) stunned [H.name] ([H.ckey])" )
|
||||
msg_admin_attack("Flying [src.name], last touched by ([src.fingerprintslast]) stunned [key_name(H)]" )
|
||||
|
||||
/obj/item/weapon/melee/baton/emp_act(severity)
|
||||
switch(severity)
|
||||
|
||||
@@ -620,6 +620,35 @@ LOOK FOR SURGERY.DM*/
|
||||
return
|
||||
*/
|
||||
|
||||
/*
|
||||
* Researchable Scalpels
|
||||
*/
|
||||
/obj/item/weapon/scalpel/laser1
|
||||
name = "laser scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks basic and could be improved."
|
||||
icon_state = "scalpel_laser1_on"
|
||||
damtype = "fire"
|
||||
|
||||
/obj/item/weapon/scalpel/laser2
|
||||
name = "laser scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks somewhat advanced."
|
||||
icon_state = "scalpel_laser2_on"
|
||||
damtype = "fire"
|
||||
force = 12.0
|
||||
|
||||
/obj/item/weapon/scalpel/laser3
|
||||
name = "laser scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks to be the pinnacle of precision energy cutlery!"
|
||||
icon_state = "scalpel_laser3_on"
|
||||
damtype = "fire"
|
||||
force = 15.0
|
||||
|
||||
/obj/item/weapon/scalpel/manager
|
||||
name = "incision management system"
|
||||
desc = "A true extension of the surgeon's body, this marvel instantly and completely prepares an incision allowing for the immediate commencement of therapeutic steps."
|
||||
icon_state = "scalpel_manager_on"
|
||||
force = 7.5
|
||||
|
||||
/*
|
||||
* Circular Saw
|
||||
*/
|
||||
@@ -819,4 +848,4 @@ LOOK FOR SURGERY.DM*/
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
w_class = 2.0
|
||||
attack_verb = list("attacked", "hit", "bludgeoned")
|
||||
attack_verb = list("attacked", "hit", "bludgeoned")
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
M.Weaken(5)
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been attacked with [src.name] by [user.name] ([user.ckey])</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to attack [M.name] ([M.ckey])</font>")
|
||||
log_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])")
|
||||
msg_admin_attack("[key_name(user)] attacked [key_name(user)] with [src.name] (INTENT: [uppertext(user.a_intent)])")
|
||||
src.add_fingerprint(user)
|
||||
|
||||
for(var/mob/O in viewers(M))
|
||||
@@ -153,7 +153,7 @@
|
||||
playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
|
||||
add_fingerprint(user)
|
||||
|
||||
if(blood_overlay && (blood_DNA.len >= 1)) //updates blood overlay, if any
|
||||
if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any
|
||||
overlays.Cut()//this might delete other item overlays as well but eeeeeeeh
|
||||
|
||||
var/icon/I = new /icon(src.icon, src.icon_state)
|
||||
@@ -265,4 +265,4 @@
|
||||
H.update_inv_r_hand()
|
||||
|
||||
add_fingerprint(user)
|
||||
return
|
||||
return
|
||||
|
||||
@@ -268,10 +268,12 @@
|
||||
if(H.belt)
|
||||
if(H.belt.clean_blood())
|
||||
H.update_inv_belt(0)
|
||||
H.clean_blood(washshoes)
|
||||
else
|
||||
if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags
|
||||
if(M.wear_mask.clean_blood())
|
||||
M.update_inv_wear_mask(0)
|
||||
M.clean_blood()
|
||||
else
|
||||
O.clean_blood()
|
||||
|
||||
@@ -413,4 +415,4 @@
|
||||
/obj/structure/sink/puddle/attackby(obj/item/O as obj, mob/user as mob)
|
||||
icon_state = "puddle-splash"
|
||||
..()
|
||||
icon_state = "puddle"
|
||||
icon_state = "puddle"
|
||||
@@ -195,6 +195,8 @@
|
||||
if(L)
|
||||
del L
|
||||
|
||||
var/turf_light_data/old_lights = new
|
||||
|
||||
//Creates a new turf
|
||||
/turf/proc/ChangeTurf(var/turf/N)
|
||||
if (!N)
|
||||
@@ -214,7 +216,20 @@
|
||||
return W
|
||||
///// Z-Level Stuff
|
||||
|
||||
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
|
||||
//world << "Replacing [src.type] with [N]"
|
||||
|
||||
var/old_opacity = opacity
|
||||
|
||||
old_lights.copy_from(src)
|
||||
|
||||
if(connections) connections.erase_all()
|
||||
|
||||
if(istype(src,/turf/simulated))
|
||||
//Yeah, we're just going to rebuild the whole thing.
|
||||
//Despite this being called a bunch during explosions,
|
||||
//the zone will only really do heavy lifting once.
|
||||
var/turf/simulated/S = src
|
||||
if(S.zone) S.zone.rebuild()
|
||||
|
||||
if(ispath(N, /turf/simulated/floor))
|
||||
//if the old turf had a zone, connect the new turf to it as well - Cael
|
||||
@@ -227,10 +242,12 @@
|
||||
var/turf/simulated/W = new N( locate(src.x, src.y, src.z) )
|
||||
//W.Assimilate_Air()
|
||||
|
||||
W.lighting_lumcount += old_lumcount
|
||||
if(old_lumcount != W.lighting_lumcount)
|
||||
W.lighting_changed = 1
|
||||
lighting_controller.changed_turfs += W
|
||||
old_lights.copy_to(W)
|
||||
W.ResetAllLights()
|
||||
|
||||
if(old_opacity)
|
||||
W.opacity = 1
|
||||
W.SetOpacity(0)
|
||||
|
||||
if (istype(W,/turf/simulated/floor))
|
||||
W.RemoveLattice()
|
||||
@@ -248,10 +265,9 @@
|
||||
// zone.SetStatus(ZONE_ACTIVE)
|
||||
|
||||
var/turf/W = new N( locate(src.x, src.y, src.z) )
|
||||
W.lighting_lumcount += old_lumcount
|
||||
if(old_lumcount != W.lighting_lumcount)
|
||||
W.lighting_changed = 1
|
||||
lighting_controller.changed_turfs += W
|
||||
|
||||
old_lights.copy_to(W)
|
||||
W.ResetAllLights()
|
||||
|
||||
if(air_master)
|
||||
air_master.mark_for_update(src)
|
||||
|
||||
@@ -51,7 +51,7 @@ world/IsBanned(key,address,computer_id)
|
||||
failedcid = 0
|
||||
cidquery = " OR computerid = '[computer_id]' "
|
||||
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM erro_Ban WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM erro_ban WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
|
||||
|
||||
query.Execute()
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ var/list/admin_verbs_admin = list(
|
||||
/client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
|
||||
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
|
||||
/client/proc/giveruntimelog, /*allows us to give access to runtime logs to somebody*/
|
||||
/client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/
|
||||
/client/proc/getserverlog, /*allows us to fetch server logs (diary) for other days*/
|
||||
/client/proc/jumptocoord, /*we ghost and jump to a coordinate*/
|
||||
/client/proc/Getmob, /*teleports a mob to our location*/
|
||||
@@ -89,6 +88,7 @@ var/list/admin_verbs_fun = list(
|
||||
/client/proc/cmd_admin_dress,
|
||||
/client/proc/cmd_admin_gib_self,
|
||||
/client/proc/drop_bomb,
|
||||
/client/proc/everyone_random,
|
||||
/client/proc/cinematic,
|
||||
/client/proc/one_click_antag,
|
||||
/datum/admins/proc/toggle_aliens,
|
||||
@@ -127,6 +127,7 @@ var/list/admin_verbs_server = list(
|
||||
/client/proc/check_customitem_activity
|
||||
)
|
||||
var/list/admin_verbs_debug = list(
|
||||
/client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/
|
||||
/client/proc/cmd_admin_list_open_jobs,
|
||||
/client/proc/Debug2,
|
||||
/client/proc/kill_air,
|
||||
|
||||
@@ -125,20 +125,20 @@ var/intercom_range_display_status = 0
|
||||
feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
var/list/debug_verbs = list (
|
||||
/client/proc/do_not_use_these
|
||||
,/client/proc/camera_view
|
||||
/client/proc/do_not_use_these
|
||||
,/client/proc/camera_view
|
||||
,/client/proc/sec_camera_report
|
||||
,/client/proc/intercom_view
|
||||
,/client/proc/air_status
|
||||
,/client/proc/Cell
|
||||
,/client/proc/atmosscan
|
||||
,/client/proc/powerdebug
|
||||
,/client/proc/intercom_view
|
||||
,/client/proc/air_status
|
||||
,/client/proc/Cell
|
||||
,/client/proc/atmosscan
|
||||
,/client/proc/powerdebug
|
||||
,/client/proc/count_objects_on_z_level
|
||||
,/client/proc/count_objects_all
|
||||
,/client/proc/cmd_assume_direct_control
|
||||
,/client/proc/jump_to_dead_group
|
||||
,/client/proc/startSinglo
|
||||
,/client/proc/ticklag
|
||||
,/client/proc/ticklag
|
||||
,/client/proc/cmd_admin_grantfullaccess
|
||||
,/client/proc/kaboom
|
||||
,/client/proc/splash
|
||||
@@ -157,9 +157,10 @@ var/list/debug_verbs = list (
|
||||
,/client/proc/Zone_Info
|
||||
,/client/proc/Test_ZAS_Connection
|
||||
,/client/proc/ZoneTick
|
||||
,/client/proc/rebootAirMaster
|
||||
,/client/proc/hide_debug_verbs
|
||||
,/client/proc/testZAScolors
|
||||
,/client/proc/testZAScolors_remove
|
||||
,/client/proc/testZAScolors
|
||||
,/client/proc/testZAScolors_remove
|
||||
)
|
||||
|
||||
|
||||
@@ -193,16 +194,17 @@ var/list/debug_verbs = list (
|
||||
testZAScolors_zones += Z
|
||||
if(recurse_level > 10)
|
||||
return
|
||||
var/icon/yellow = new('icons/misc/debug_group.dmi', "yellow")
|
||||
var/icon/yellow = new('icons/misc/debug_group.dmi', "yellow")
|
||||
|
||||
for(var/turf/T in Z.contents)
|
||||
images += image(yellow, T, "zasdebug", TURF_LAYER)
|
||||
testZAScolors_turfs += T
|
||||
for(var/zone/connected in Z.connected_zones)
|
||||
for(var/connection_edge/zone/edge in Z.edges)
|
||||
var/zone/connected = edge.get_connected_zone(Z)
|
||||
if(connected in testZAScolors_zones)
|
||||
continue
|
||||
recurse_zone(connected,recurse_level+1)
|
||||
|
||||
|
||||
|
||||
/client/proc/testZAScolors()
|
||||
set category = "ZAS"
|
||||
@@ -211,17 +213,17 @@ var/list/debug_verbs = list (
|
||||
if(!check_rights(R_DEBUG)) return
|
||||
testZAScolors_remove()
|
||||
|
||||
var/turf/location = get_turf(usr)
|
||||
|
||||
if(!istype(location, /turf/simulated)) // We're in space, let's not cause runtimes.
|
||||
usr << "\red this debug tool cannot be used from space"
|
||||
return
|
||||
var/turf/simulated/location = get_turf(usr)
|
||||
|
||||
if(!istype(location, /turf/simulated)) // We're in space, let's not cause runtimes.
|
||||
usr << "\red this debug tool cannot be used from space"
|
||||
return
|
||||
|
||||
var/icon/red = new('icons/misc/debug_group.dmi', "red") //created here so we don't have to make thousands of these.
|
||||
var/icon/green = new('icons/misc/debug_group.dmi', "green")
|
||||
var/icon/blue = new('icons/misc/debug_group.dmi', "blue")
|
||||
var/icon/green = new('icons/misc/debug_group.dmi', "green")
|
||||
var/icon/blue = new('icons/misc/debug_group.dmi', "blue")
|
||||
|
||||
if(!usedZAScolors)
|
||||
if(!usedZAScolors)
|
||||
usr << "ZAS Test Colors"
|
||||
usr << "Green = Zone you are standing in"
|
||||
usr << "Blue = Connected zone to the zone you are standing in"
|
||||
@@ -233,12 +235,14 @@ var/list/debug_verbs = list (
|
||||
for(var/turf/T in location.zone.contents)
|
||||
images += image(green, T,"zasdebug", TURF_LAYER)
|
||||
testZAScolors_turfs += T
|
||||
for(var/zone/Z in location.zone.connected_zones)
|
||||
testZAScolors_zones += Z
|
||||
for(var/connection_edge/zone/edge in location.zone.edges)
|
||||
var/zone/Z = edge.get_connected_zone(location.zone)
|
||||
testZAScolors_zones += Z
|
||||
for(var/turf/T in Z.contents)
|
||||
images += image(blue, T,"zasdebug",TURF_LAYER)
|
||||
testZAScolors_turfs += T
|
||||
for(var/zone/connected in Z.connected_zones)
|
||||
for(var/connection_edge/zone/z_edge in Z.edges)
|
||||
var/zone/connected = z_edge.get_connected_zone(Z)
|
||||
if(connected in testZAScolors_zones)
|
||||
continue
|
||||
recurse_zone(connected,1)
|
||||
@@ -263,6 +267,19 @@ var/list/debug_verbs = list (
|
||||
if(i.icon_state == "zasdebug")
|
||||
images.Remove(i)
|
||||
|
||||
/client/proc/rebootAirMaster()
|
||||
set category = "ZAS"
|
||||
set name = "Reboot ZAS"
|
||||
|
||||
if(alert("This will destroy and remake all zone geometry on the whole map.","Reboot ZAS","Reboot ZAS","Nevermind") == "Reboot ZAS")
|
||||
var/datum/controller/air_system/old_air = air_master
|
||||
for(var/zone/zone in old_air.zones)
|
||||
zone.c_invalidate()
|
||||
del old_air
|
||||
air_master = new
|
||||
air_master.Setup()
|
||||
spawn air_master.Start()
|
||||
|
||||
|
||||
/client/proc/count_objects_on_z_level()
|
||||
set category = "Mapping"
|
||||
|
||||
@@ -250,9 +250,9 @@ datum/preferences
|
||||
dat += "-Alpha(transparency): <a href='?_src_=prefs;preference=UIalpha'><b>[UI_style_alpha]</b></a><br>"
|
||||
dat += "<b>Play admin midis:</b> <a href='?_src_=prefs;preference=hear_midis'><b>[(toggles & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>"
|
||||
dat += "<b>Play lobby music:</b> <a href='?_src_=prefs;preference=lobby_music'><b>[(toggles & SOUND_LOBBY) ? "Yes" : "No"]</b></a><br>"
|
||||
dat += "<b>Ghost ears:</b> <a href='?_src_=prefs;preference=ghost_ears'><b>[(toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]</b></a><br>"
|
||||
dat += "<b>Ghost sight:</b> <a href='?_src_=prefs;preference=ghost_sight'><b>[(toggles & CHAT_GHOSTSIGHT) ? "Nearest Creatures" : "All Emotes"]</b></a><br>"
|
||||
dat += "<b>Ghost radio:</b> <a href='?_src_=prefs;preference=ghost_radio'><b>[(toggles & CHAT_GHOSTRADIO) ? "Nearest Speakers" : "All Chatter"]</b></a><br>"
|
||||
dat += "<b>Ghost ears:</b> <a href='?_src_=prefs;preference=ghost_ears'><b>[(toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]</b></a><br>"
|
||||
dat += "<b>Ghost sight:</b> <a href='?_src_=prefs;preference=ghost_sight'><b>[(toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]</b></a><br>"
|
||||
dat += "<b>Ghost radio:</b> <a href='?_src_=prefs;preference=ghost_radio'><b>[(toggles & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearest Speakers"]</b></a><br>"
|
||||
|
||||
if(config.allow_Metadata)
|
||||
dat += "<b>OOC Notes:</b> <a href='?_src_=prefs;preference=metadata;task=input'> Edit </a><br>"
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
icon_state = "hipster_glasses"
|
||||
item_state = "hipster_glasses"
|
||||
|
||||
/obj/item/clothing/glasses/threedglasses
|
||||
desc = "A long time ago, people used these glasses to makes images from screens threedimensional."
|
||||
name = "3D glasses"
|
||||
icon_state = "3d"
|
||||
item_state = "3d"
|
||||
|
||||
/obj/item/clothing/glasses/gglasses
|
||||
name = "Green Glasses"
|
||||
desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme."
|
||||
|
||||
@@ -190,7 +190,40 @@
|
||||
desc = "A suit of armor most often used by Special Weapons and Tactics squads. Includes padded vest with pockets along with shoulder and kneeguards."
|
||||
icon_state = "swatarmor"
|
||||
item_state = "armor"
|
||||
var/obj/item/weapon/gun/holstered = null
|
||||
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
|
||||
slowdown = 1
|
||||
armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 20, bio = 0, rad = 0)
|
||||
siemens_coefficient = 0.7
|
||||
siemens_coefficient = 0.7
|
||||
|
||||
/obj/item/clothing/suit/armor/tactical/verb/holster()
|
||||
set name = "Holster"
|
||||
set category = "Object"
|
||||
set src in usr
|
||||
if(!istype(usr, /mob/living)) return
|
||||
if(usr.stat) return
|
||||
|
||||
if(!holstered)
|
||||
if(!istype(usr.get_active_hand(), /obj/item/weapon/gun))
|
||||
usr << "\blue You need your gun equiped to holster it."
|
||||
return
|
||||
var/obj/item/weapon/gun/W = usr.get_active_hand()
|
||||
if (!W.isHandgun())
|
||||
usr << "\red This gun won't fit in \the belt!"
|
||||
return
|
||||
holstered = usr.get_active_hand()
|
||||
usr.drop_item()
|
||||
holstered.loc = src
|
||||
usr.visible_message("\blue \The [usr] holsters \the [holstered].", "You holster \the [holstered].")
|
||||
else
|
||||
if(istype(usr.get_active_hand(),/obj) && istype(usr.get_inactive_hand(),/obj))
|
||||
usr << "\red You need an empty hand to draw the gun!"
|
||||
else
|
||||
if(usr.a_intent == "hurt")
|
||||
usr.visible_message("\red \The [usr] draws \the [holstered], ready to shoot!", \
|
||||
"\red You draw \the [holstered], ready to shoot!")
|
||||
else
|
||||
usr.visible_message("\blue \The [usr] draws \the [holstered], pointing it at the ground.", \
|
||||
"\blue You draw \the [holstered], pointing it at the ground.")
|
||||
usr.put_in_hands(holstered)
|
||||
holstered = null
|
||||
@@ -17,7 +17,7 @@
|
||||
var/worth = 0
|
||||
|
||||
/obj/item/weapon/spacecash/c1
|
||||
name = "1 credip chip"
|
||||
name = "1 credit chip"
|
||||
icon_state = "spacecash"
|
||||
desc = "It's worth 1 credit."
|
||||
worth = 1
|
||||
|
||||
@@ -38,15 +38,13 @@
|
||||
|
||||
// Type 1 (Visual) emotes are sent to anyone in view of the item
|
||||
if (m_type & 1)
|
||||
var/list/can_see = get_mobs_in_view(1,src) //Allows silicon & mmi mobs carried around to see the emotes of the person carrying them around.
|
||||
can_see |= viewers(src,null)
|
||||
for (var/mob/O in can_see)
|
||||
for (var/mob/O in viewers(src, null))
|
||||
O.show_message(message, m_type)
|
||||
|
||||
// Type 2 (Audible) emotes are sent to anyone in hear range
|
||||
// of the *LOCATION* -- this is important for pAIs to be heard
|
||||
else if (m_type & 2)
|
||||
for (var/mob/O in get_mobs_in_view(7,src))
|
||||
for (var/mob/O in hearers(get_turf(src), null))
|
||||
O.show_message(message, m_type)
|
||||
|
||||
/mob/proc/emote_dead(var/message)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
if(!dna)
|
||||
dna = new /datum/dna(null)
|
||||
dna.species=species.name
|
||||
dna.species=species.name
|
||||
|
||||
for(var/i=0;i<7;i++) // 2 for medHUDs and 5 for secHUDs
|
||||
hud_list += image('icons/mob/hud.dmi', src, "hudunknown")
|
||||
@@ -1113,10 +1113,12 @@
|
||||
verbs += /mob/living/carbon/human/proc/bloody_doodle
|
||||
return 1 //we applied blood to the item
|
||||
|
||||
/mob/living/carbon/human/clean_blood()
|
||||
/mob/living/carbon/human/clean_blood(var/clean_feet)
|
||||
.=..()
|
||||
if(istype(feet_blood_DNA, /list) && feet_blood_DNA.len)
|
||||
if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len)
|
||||
feet_blood_color = null
|
||||
del(feet_blood_DNA)
|
||||
update_inv_shoes(1)
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/get_visible_implants(var/class = 0)
|
||||
@@ -1193,7 +1195,7 @@
|
||||
if(!new_species)
|
||||
new_species = dna.species
|
||||
else
|
||||
dna.species = new_species
|
||||
dna.species = new_species
|
||||
|
||||
if(species && (species.name && species.name == new_species))
|
||||
return
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>[M.species.attack_verb]ed [src.name] ([src.ckey])</font>")
|
||||
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been [M.species.attack_verb]ed by [M.name] ([M.ckey])</font>")
|
||||
log_attack("[M.name] ([M.ckey]) [M.species.attack_verb]ed [src.name] ([src.ckey])")
|
||||
msg_admin_attack("[key_name(M)] [M.species.attack_verb]ed [key_name(src)]")
|
||||
|
||||
var/damage = rand(0, 5)//BS12 EDIT
|
||||
if(!damage)
|
||||
@@ -153,7 +153,7 @@
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>Disarmed [src.name] ([src.ckey])</font>")
|
||||
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been disarmed by [M.name] ([M.ckey])</font>")
|
||||
|
||||
log_attack("[M.name] ([M.ckey]) disarmed [src.name] ([src.ckey])")
|
||||
msg_admin_attack("[key_name(M)] disarmed [src.name] ([src.ckey])")
|
||||
|
||||
if(w_uniform)
|
||||
w_uniform.add_fingerprint(M)
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
|
||||
breath = loc.remove_air(breath_moles)
|
||||
|
||||
if(istype(wear_mask, /obj/item/clothing/mask/gas))
|
||||
if(istype(wear_mask, /obj/item/clothing/mask/gas) && breath)
|
||||
var/obj/item/clothing/mask/gas/G = wear_mask
|
||||
var/datum/gas_mixture/filtered = new
|
||||
|
||||
@@ -897,7 +897,7 @@
|
||||
var/turf/T = loc
|
||||
var/area/A = T.loc
|
||||
if(A)
|
||||
if(A.lighting_use_dynamic) light_amount = min(10,T.lighting_lumcount) - 5 //hardcapped so it's not abused by having a ton of flashlights
|
||||
if(A.lighting_use_dynamic) light_amount = min(10,T.lit_value) - 5 //hardcapped so it's not abused by having a ton of flashlights
|
||||
else light_amount = 5
|
||||
nutrition += light_amount
|
||||
traumatic_shock -= light_amount
|
||||
@@ -917,7 +917,7 @@
|
||||
var/turf/T = loc
|
||||
var/area/A = T.loc
|
||||
if(A)
|
||||
if(A.lighting_use_dynamic) light_amount = T.lighting_lumcount
|
||||
if(A.lighting_use_dynamic) light_amount = T.lit_value
|
||||
else light_amount = 10
|
||||
if(light_amount > 2) //if there's enough light, start dying
|
||||
take_overall_damage(1,1)
|
||||
@@ -1072,7 +1072,7 @@
|
||||
E = get_visible_implants(0)
|
||||
if(!E.len)
|
||||
embedded_flag = 0
|
||||
|
||||
|
||||
|
||||
//Eyes
|
||||
if(sdisabilities & BLIND) //disabled-blind, doesn't get better on its own
|
||||
@@ -1393,7 +1393,7 @@
|
||||
//0.1% chance of playing a scary sound to someone who's in complete darkness
|
||||
if(isturf(loc) && rand(1,1000) == 1)
|
||||
var/turf/currentTurf = loc
|
||||
if(!currentTurf.lighting_lumcount)
|
||||
if(!currentTurf.lit_value)
|
||||
playsound_local(src,pick(scarySounds),50, 1, -1)
|
||||
|
||||
proc/handle_virus_updates()
|
||||
|
||||
@@ -446,7 +446,7 @@
|
||||
var/turf/T = loc
|
||||
var/area/A = T.loc
|
||||
if(A)
|
||||
if(A.lighting_use_dynamic) light_amount = min(10,T.lighting_lumcount) - 5 //hardcapped so it's not abused by having a ton of flashlights
|
||||
if(A.lighting_use_dynamic) light_amount = min(10,T.lit_value) - 5 //hardcapped so it's not abused by having a ton of flashlights
|
||||
else light_amount = 5
|
||||
|
||||
nutrition += light_amount
|
||||
|
||||
@@ -256,8 +256,15 @@
|
||||
buckled = initial(src.buckled)
|
||||
if(iscarbon(src))
|
||||
var/mob/living/carbon/C = src
|
||||
|
||||
if (C.handcuffed && !initial(C.handcuffed))
|
||||
C.drop_from_inventory(C.handcuffed)
|
||||
C.handcuffed = initial(C.handcuffed)
|
||||
|
||||
if (C.legcuffed && !initial(C.legcuffed))
|
||||
C.drop_from_inventory(C.legcuffed)
|
||||
C.legcuffed = initial(C.legcuffed)
|
||||
|
||||
/mob/living/proc/rejuvenate()
|
||||
|
||||
// shut down various types of badness
|
||||
@@ -627,8 +634,6 @@
|
||||
O.show_message("\red <B>[CM] manages to remove the handcuffs!</B>", 1)
|
||||
CM << "\blue You successfully remove \the [CM.handcuffed]."
|
||||
CM.drop_from_inventory(CM.handcuffed)
|
||||
CM.handcuffed = null
|
||||
CM.update_inv_handcuffed()
|
||||
|
||||
else if(CM.legcuffed && CM.canmove && (CM.last_special <= world.time))
|
||||
CM.next_move = world.time + 100
|
||||
@@ -766,4 +771,4 @@
|
||||
loc = target_vent.loc
|
||||
var/area/new_area = get_area(loc)
|
||||
if(new_area)
|
||||
new_area.Entered(src)
|
||||
new_area.Entered(src)
|
||||
|
||||
@@ -384,19 +384,20 @@ var/list/department_radio_keys = list(
|
||||
rendered = "<span class='game say'><span class='name'>[GetVoice()]</span>[alt_name] <span class='message'>[message_a]</span></span>"
|
||||
var/rendered_ghost = "<span class='game say'><span class='name'>[GetVoice()]</span>[alt_name] <span class='message'>[message_ghost]</span></span>"
|
||||
for (var/mob/M in heard_a)
|
||||
if(hascall(M,"show_message"))
|
||||
var/deaf_message = ""
|
||||
var/deaf_type = 1
|
||||
if(M != src)
|
||||
deaf_message = "<span class='name'>[name]</span>[alt_name] talks but you cannot hear them."
|
||||
else
|
||||
deaf_message = "<span class='notice'>You cannot hear yourself!</span>"
|
||||
deaf_type = 2 // Since you should be able to hear yourself without looking
|
||||
if (M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTEARS) && M in onscreen)
|
||||
M:show_message(rendered_ghost, 2, deaf_message, deaf_type)
|
||||
else
|
||||
M:show_message(rendered, 2, deaf_message, deaf_type)
|
||||
M << speech_bubble
|
||||
if(M.client)
|
||||
if(hascall(M,"show_message"))
|
||||
var/deaf_message = ""
|
||||
var/deaf_type = 1
|
||||
if(M != src)
|
||||
deaf_message = "<span class='name'>[name]</span>[alt_name] talks but you cannot hear them."
|
||||
else
|
||||
deaf_message = "<span class='notice'>You cannot hear yourself!</span>"
|
||||
deaf_type = 2 // Since you should be able to hear yourself without looking
|
||||
if (M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTEARS) && M in onscreen)
|
||||
M:show_message(rendered_ghost, 2, deaf_message, deaf_type)
|
||||
else
|
||||
M:show_message(rendered, 2, deaf_message, deaf_type)
|
||||
M << speech_bubble
|
||||
|
||||
if (length(heard_b))
|
||||
|
||||
|
||||
@@ -1180,7 +1180,7 @@
|
||||
if(cleaned_human.shoes)
|
||||
cleaned_human.shoes.clean_blood()
|
||||
cleaned_human.update_inv_shoes(0)
|
||||
cleaned_human.clean_blood()
|
||||
cleaned_human.clean_blood(1)
|
||||
cleaned_human << "\red [src] cleans your face!"
|
||||
return
|
||||
|
||||
|
||||
@@ -88,9 +88,10 @@
|
||||
|
||||
for(var/this_type in calmers)
|
||||
var/mob/living/simple_animal/check_effect = locate() in src.loc
|
||||
if(check_effect.type == this_type)
|
||||
calming = 1
|
||||
break
|
||||
if(istype(check_effect))
|
||||
if(check_effect.type == this_type)
|
||||
calming = 1
|
||||
break
|
||||
|
||||
if(calming)
|
||||
if(feral > 0)
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
affecting.loc = assailant.loc
|
||||
affecting.attack_log += "\[[time_stamp()]\] <font color='orange'>Has had their neck grabbed by [assailant.name] ([assailant.ckey])</font>"
|
||||
assailant.attack_log += "\[[time_stamp()]\] <font color='red'>Grabbed the neck of [affecting.name] ([affecting.ckey])</font>"
|
||||
log_attack("<font color='red'>[assailant.name] ([assailant.ckey]) grabbed the neck of [affecting.name] ([affecting.ckey])</font>")
|
||||
msg_admin_attack("[key_name(assailant)] grabbed the neck of [key_name(affecting)]")
|
||||
hud.icon_state = "disarm/kill"
|
||||
hud.name = "disarm/kill"
|
||||
else
|
||||
@@ -158,7 +158,7 @@
|
||||
assailant.visible_message("<span class='danger'>[assailant] has tightened \his grip on [affecting]'s neck!</span>")
|
||||
affecting.attack_log += "\[[time_stamp()]\] <font color='orange'>Has been strangled (kill intent) by [assailant.name] ([assailant.ckey])</font>"
|
||||
assailant.attack_log += "\[[time_stamp()]\] <font color='red'>Strangled (kill intent) [affecting.name] ([affecting.ckey])</font>"
|
||||
log_attack("<font color='red'>[assailant.name] ([assailant.ckey]) Strangled (kill intent) [affecting.name] ([affecting.ckey])</font>")
|
||||
msg_admin_attack("[key_name(assailant)] strangled (kill intent) [key_name(affecting)]")
|
||||
|
||||
assailant.next_move = world.time + 10
|
||||
affecting.losebreath += 1
|
||||
|
||||
@@ -570,11 +570,28 @@ This function completely restores a damaged organ to perfect condition.
|
||||
|
||||
owner.update_body(1)
|
||||
|
||||
// OK so maybe your limb just flew off, but if it was attached to a pair of cuffs then hooray! Freedom!
|
||||
release_restraints()
|
||||
|
||||
/****************************************************
|
||||
HELPERS
|
||||
****************************************************/
|
||||
|
||||
/datum/organ/external/proc/release_restraints()
|
||||
if (owner.handcuffed && body_part in list(ARM_LEFT, ARM_RIGHT, HAND_LEFT, HAND_RIGHT))
|
||||
owner.visible_message(\
|
||||
"\The [owner.handcuffed.name] falls off of [owner.name].",\
|
||||
"\The [owner.handcuffed.name] falls off you.")
|
||||
|
||||
owner.drop_from_inventory(owner.handcuffed)
|
||||
|
||||
if (owner.legcuffed && body_part in list(FOOT_LEFT, FOOT_RIGHT, LEG_LEFT, LEG_RIGHT))
|
||||
owner.visible_message(\
|
||||
"\The [owner.legcuffed.name] falls off of [owner.name].",\
|
||||
"\The [owner.legcuffed.name] falls off you.")
|
||||
|
||||
owner.drop_from_inventory(owner.legcuffed)
|
||||
|
||||
/datum/organ/external/proc/bandage()
|
||||
var/rval = 0
|
||||
src.status &= ~ORGAN_BLEEDING
|
||||
@@ -603,7 +620,10 @@ This function completely restores a damaged organ to perfect condition.
|
||||
/datum/organ/external/proc/fracture()
|
||||
if(status & ORGAN_BROKEN)
|
||||
return
|
||||
owner.visible_message("\red You hear a loud cracking sound coming from \the [owner].","\red <b>Something feels like it shattered in your [display_name]!</b>","You hear a sickening crack.")
|
||||
owner.visible_message(\
|
||||
"\red You hear a loud cracking sound coming from \the [owner].",\
|
||||
"\red <b>Something feels like it shattered in your [display_name]!</b>",\
|
||||
"You hear a sickening crack.")
|
||||
|
||||
if(owner.species && !(owner.species.flags & NO_PAIN))
|
||||
owner.emote("scream")
|
||||
@@ -612,6 +632,10 @@ This function completely restores a damaged organ to perfect condition.
|
||||
broken_description = pick("broken","fracture","hairline fracture")
|
||||
perma_injury = brute_dam
|
||||
|
||||
// Fractures have a chance of getting you out of restraints
|
||||
if (prob(25))
|
||||
release_restraints()
|
||||
|
||||
/datum/organ/external/proc/robotize()
|
||||
src.status &= ~ORGAN_BROKEN
|
||||
src.status &= ~ORGAN_BLEEDING
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
icon_state = "ice_1"
|
||||
damage = 20
|
||||
flag = "energy"
|
||||
embed = 1
|
||||
|
||||
/obj/item/projectile/forcebolt/strong
|
||||
name = "force bolt"
|
||||
|
||||
@@ -269,7 +269,7 @@ client/proc/add_gun_icons()
|
||||
screen += usr.gun_run_icon
|
||||
|
||||
client/proc/remove_gun_icons()
|
||||
if(isnull(usr)) return 1 // Runtime prevention on N00k agents spawning with SMG
|
||||
if(!usr) return 1 // Runtime prevention on N00k agents spawning with SMG
|
||||
screen -= usr.item_use_icon
|
||||
screen -= usr.gun_move_icon
|
||||
if (target_can_move)
|
||||
|
||||
@@ -35,31 +35,32 @@ datum
|
||||
var/datum/reagent/self = src
|
||||
src = null //of the reagent to the mob on TOUCHING it.
|
||||
|
||||
if(!istype(self.holder.my_atom, /obj/effect/effect/smoke/chem))
|
||||
// If the chemicals are in a smoke cloud, do not try to let the chemicals "penetrate" into the mob's system (balance station 13) -- Doohl
|
||||
if(self.holder) //for catching rare runtimes
|
||||
if(!istype(self.holder.my_atom, /obj/effect/effect/smoke/chem))
|
||||
// If the chemicals are in a smoke cloud, do not try to let the chemicals "penetrate" into the mob's system (balance station 13) -- Doohl
|
||||
|
||||
if(method == TOUCH)
|
||||
if(method == TOUCH)
|
||||
|
||||
var/chance = 1
|
||||
var/block = 0
|
||||
var/chance = 1
|
||||
var/block = 0
|
||||
|
||||
for(var/obj/item/clothing/C in M.get_equipped_items())
|
||||
if(C.permeability_coefficient < chance) chance = C.permeability_coefficient
|
||||
if(istype(C, /obj/item/clothing/suit/bio_suit))
|
||||
// bio suits are just about completely fool-proof - Doohl
|
||||
// kind of a hacky way of making bio suits more resistant to chemicals but w/e
|
||||
if(prob(75))
|
||||
block = 1
|
||||
for(var/obj/item/clothing/C in M.get_equipped_items())
|
||||
if(C.permeability_coefficient < chance) chance = C.permeability_coefficient
|
||||
if(istype(C, /obj/item/clothing/suit/bio_suit))
|
||||
// bio suits are just about completely fool-proof - Doohl
|
||||
// kind of a hacky way of making bio suits more resistant to chemicals but w/e
|
||||
if(prob(75))
|
||||
block = 1
|
||||
|
||||
if(istype(C, /obj/item/clothing/head/bio_hood))
|
||||
if(prob(75))
|
||||
block = 1
|
||||
if(istype(C, /obj/item/clothing/head/bio_hood))
|
||||
if(prob(75))
|
||||
block = 1
|
||||
|
||||
chance = chance * 100
|
||||
chance = chance * 100
|
||||
|
||||
if(prob(chance) && !block)
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent(self.id,self.volume/2)
|
||||
if(prob(chance) && !block)
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent(self.id,self.volume/2)
|
||||
return 1
|
||||
|
||||
reaction_obj(var/obj/O, var/volume) //By default we transfer a small part of the reagent to the object
|
||||
@@ -582,7 +583,12 @@ datum
|
||||
reaction_turf(var/turf/T, var/volume)
|
||||
src = null
|
||||
if(!istype(T, /turf/space))
|
||||
new /obj/effect/decal/cleanable/dirt(T)
|
||||
var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, T)
|
||||
if (!dirtoverlay)
|
||||
dirtoverlay = new/obj/effect/decal/cleanable/dirt(T)
|
||||
dirtoverlay.alpha = volume*30
|
||||
else
|
||||
dirtoverlay.alpha = min(dirtoverlay.alpha+volume*30, 255)
|
||||
|
||||
chlorine
|
||||
name = "Chlorine"
|
||||
@@ -708,7 +714,9 @@ datum
|
||||
src = null
|
||||
if(volume >= 3)
|
||||
if(!istype(T, /turf/space))
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
var/obj/effect/decal/cleanable/greenglow/glow = locate(/obj/effect/decal/cleanable/greenglow, T)
|
||||
if(!glow)
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
return
|
||||
|
||||
|
||||
@@ -748,9 +756,9 @@ datum
|
||||
src = null
|
||||
if(volume >= 5)
|
||||
if(istype(T, /turf/simulated/wall))
|
||||
T:thermite = 1
|
||||
T.overlays.Cut()
|
||||
T.overlays = image('icons/effects/effects.dmi',icon_state = "thermite")
|
||||
var/turf/simulated/wall/W = T
|
||||
W.thermite = 1
|
||||
W.overlays += image('icons/effects/effects.dmi',icon_state = "#673910")
|
||||
return
|
||||
|
||||
on_mob_life(var/mob/living/M as mob)
|
||||
@@ -872,7 +880,10 @@ datum
|
||||
src = null
|
||||
if(volume >= 3)
|
||||
if(!istype(T, /turf/space))
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
var/obj/effect/decal/cleanable/greenglow/glow = locate(/obj/effect/decal/cleanable/greenglow, T)
|
||||
if(!glow)
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
return
|
||||
|
||||
aluminum
|
||||
name = "Aluminum"
|
||||
@@ -931,7 +942,6 @@ datum
|
||||
if(istype(T, /turf/simulated))
|
||||
var/turf/simulated/S = T
|
||||
S.dirt = 0
|
||||
T.overlays.Cut()
|
||||
T.clean_blood()
|
||||
for(var/obj/effect/decal/cleanable/C in T.contents)
|
||||
src.reaction_obj(C, volume)
|
||||
@@ -939,7 +949,7 @@ datum
|
||||
|
||||
for(var/mob/living/carbon/slime/M in T)
|
||||
M.adjustToxLoss(rand(5,10))
|
||||
|
||||
|
||||
reaction_mob(var/mob/M, var/method=TOUCH, var/volume)
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
@@ -964,6 +974,9 @@ datum
|
||||
if(H.shoes)
|
||||
if(H.shoes.clean_blood())
|
||||
H.update_inv_shoes(0)
|
||||
else
|
||||
H.clean_blood(1)
|
||||
return
|
||||
M.clean_blood()
|
||||
|
||||
leporazine
|
||||
@@ -3693,4 +3706,4 @@ datum
|
||||
return
|
||||
|
||||
// Undefine the alias for REAGENTS_EFFECT_MULTIPLER
|
||||
#undef REM
|
||||
#undef REM
|
||||
|
||||
@@ -462,8 +462,6 @@ datum
|
||||
playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3)
|
||||
spawn(0)
|
||||
S.start()
|
||||
sleep(10)
|
||||
S.start()
|
||||
holder.clear_reagents()
|
||||
return
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been fed [src.name] by [user.name] ([user.ckey]) Reagents: [reagentlist(src)]</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Fed [M.name] by [M.name] ([M.ckey]) Reagents: [reagentlist(src)]</font>")
|
||||
log_attack("[user.name] ([user.ckey]) fed [M.name] ([M.ckey]) with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
msg_admin_attack("[key_name(user)] fed [key_name(M)] with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been fed [src.name] by [user.name] ([user.ckey]) Reagents: [reagentlist(src)]</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Fed [M.name] by [M.name] ([M.ckey]) Reagents: [reagentlist(src)]</font>")
|
||||
log_attack("[user.name] ([user.ckey]) fed [M.name] ([M.ckey]) with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
msg_admin_attack("[key_name(user)] fed [key_name(M)] with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been fed [src.name] by [user.name] ([user.ckey]) Reagents: [reagentlist(src)]</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Fed [src.name] by [M.name] ([M.ckey]) Reagents: [reagentlist(src)]</font>")
|
||||
log_attack("[user.name] ([user.ckey]) fed [M.name] ([M.ckey]) with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)])")
|
||||
msg_admin_attack("[key_name(user)] fed [key_name(M)] with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)])")
|
||||
|
||||
for(var/mob/O in viewers(world.view, user))
|
||||
O.show_message("\red [user] feeds [M] [src].", 1)
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
return
|
||||
|
||||
Bumped(var/atom/movable/AM) //Go straight into the chute
|
||||
if(istype(AM, /obj/item/projectile)) return
|
||||
if(istype(AM, /obj/item/projectile) || istype(AM, /obj/effect)) return
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
if(AM.loc.y != src.loc.y+1) return
|
||||
|
||||
@@ -1398,6 +1398,43 @@ datum/design/noreactbeaker
|
||||
reliability_base = 76
|
||||
build_path = "/obj/item/weapon/reagent_containers/glass/beaker/noreact"
|
||||
category = "Misc"
|
||||
|
||||
datum/design/scalpel_laser1
|
||||
name = "Basic Laser Scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks basic and could be improved."
|
||||
id = "scalpel_laser1"
|
||||
req_tech = list("biotech" = 2, "materials" = 2, "magnets" = 2)
|
||||
build_type = PROTOLATHE
|
||||
materials = list("$metal" = 12500, "$glass" = 7500)
|
||||
build_path = "/obj/item/weapon/scalpel/laser1"
|
||||
|
||||
datum/design/scalpel_laser2
|
||||
name = "Improved Laser Scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks somewhat advanced."
|
||||
id = "scalpel_laser2"
|
||||
req_tech = list("biotech" = 3, "materials" = 4, "magnets" = 4)
|
||||
build_type = PROTOLATHE
|
||||
materials = list("$metal" = 12500, "$glass" = 7500, "$silver" = 2500)
|
||||
build_path = "/obj/item/weapon/scalpel/laser2"
|
||||
|
||||
datum/design/scalpel_laser3
|
||||
name = "Advanced Laser Scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks to be the pinnacle of precision energy cutlery!"
|
||||
id = "scalpel_laser3"
|
||||
req_tech = list("biotech" = 4, "materials" = 6, "magnets" = 5)
|
||||
build_type = PROTOLATHE
|
||||
materials = list("$metal" = 12500, "$glass" = 7500, "$silver" = 2000, "$gold" = 1500)
|
||||
build_path = "/obj/item/weapon/scalpel/laser3"
|
||||
|
||||
datum/design/scalpel_manager
|
||||
name = "Incision Management System"
|
||||
desc = "A true extension of the surgeon's body, this marvel instantly and completely prepares an incision allowing for the immediate commencement of therapeutic steps."
|
||||
id = "scalpel_manager"
|
||||
req_tech = list("biotech" = 4, "materials" = 7, "magnets" = 5, "programming" = 4)
|
||||
build_type = PROTOLATHE
|
||||
materials = list ("$metal" = 12500, "$glass" = 7500, "$silver" = 1500, "$gold" = 1500, "$diamond" = 750)
|
||||
build_path = "/obj/item/weapon/scalpel/manager"
|
||||
|
||||
/////////////////////////////////////////
|
||||
/////////////////Weapons/////////////////
|
||||
/////////////////////////////////////////
|
||||
|
||||
@@ -19,6 +19,86 @@
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/surgery_step/generic/cut_with_laser
|
||||
allowed_tools = list(
|
||||
/obj/item/weapon/scalpel/laser3 = 95, \
|
||||
/obj/item/weapon/scalpel/laser2 = 85, \
|
||||
/obj/item/weapon/scalpel/laser1 = 75, \
|
||||
/obj/item/weapon/melee/energy/sword = 5
|
||||
)
|
||||
|
||||
min_duration = 90
|
||||
max_duration = 110
|
||||
|
||||
can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
return ..() && affected.open == 0 && target_zone != "mouth"
|
||||
|
||||
begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("[user] starts the bloodless incision on [target]'s [affected.display_name] with \the [tool].", \
|
||||
"You start the bloodless incision on [target]'s [affected.display_name] with \the [tool].")
|
||||
target.custom_pain("You feel a horrible, searing pain in your [affected.display_name]!",1)
|
||||
..()
|
||||
|
||||
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\blue [user] has made a bloodless incision on [target]'s [affected.display_name] with \the [tool].", \
|
||||
"\blue You have made a bloodless incision on [target]'s [affected.display_name] with \the [tool].",)
|
||||
//Could be cleaner ...
|
||||
affected.open = 1
|
||||
affected.status |= ORGAN_BLEEDING
|
||||
affected.createwound(CUT, 1)
|
||||
affected.clamp()
|
||||
spread_germs_to_organ(affected, user)
|
||||
if (target_zone == "head")
|
||||
target.brain_op_stage = 1
|
||||
|
||||
fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\red [user]'s hand slips as the blade sputters, searing a long gash in [target]'s [affected.display_name] with \the [tool]!", \
|
||||
"\red Your hand slips as the blade sputters, searing a long gash in [target]'s [affected.display_name] with \the [tool]!")
|
||||
affected.createwound(CUT, 7.5)
|
||||
affected.createwound(BURN, 12.5)
|
||||
|
||||
/datum/surgery_step/generic/incision_manager
|
||||
allowed_tools = list(
|
||||
/obj/item/weapon/scalpel/manager = 100
|
||||
)
|
||||
|
||||
min_duration = 80
|
||||
max_duration = 120
|
||||
|
||||
can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
return ..() && affected.open == 0 && target_zone != "mouth"
|
||||
|
||||
begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("[user] starts to construct a prepared incision on and within [target]'s [affected.display_name] with \the [tool].", \
|
||||
"You start to construct a prepared incision on and within [target]'s [affected.display_name] with \the [tool].")
|
||||
target.custom_pain("You feel a horrible, searing pain in your [affected.display_name] as it is pushed apart!",1)
|
||||
..()
|
||||
|
||||
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\blue [user] has constructed a prepared incision on and within [target]'s [affected.display_name] with \the [tool].", \
|
||||
"\blue You have constructed a prepared incision on and within [target]'s [affected.display_name] with \the [tool].",)
|
||||
affected.open = 1
|
||||
affected.status |= ORGAN_BLEEDING
|
||||
affected.createwound(CUT, 1)
|
||||
affected.clamp()
|
||||
affected.open = 2
|
||||
if (target_zone == "head")
|
||||
target.brain_op_stage = 1
|
||||
|
||||
fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\red [user]'s hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.display_name] with \the [tool]!", \
|
||||
"\red Your hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.display_name] with \the [tool]!")
|
||||
affected.createwound(CUT, 20)
|
||||
affected.createwound(BURN, 15)
|
||||
|
||||
/datum/surgery_step/generic/cut_open
|
||||
allowed_tools = list(
|
||||
/obj/item/weapon/scalpel = 100, \
|
||||
@@ -219,4 +299,4 @@
|
||||
user.visible_message("\red [user]'s hand slips, sawwing through the bone in [target]'s [affected.display_name] with \the [tool]!", \
|
||||
"\red Your hand slips, sawwing through the bone in [target]'s [affected.display_name] with \the [tool]!")
|
||||
affected.createwound(CUT, 30)
|
||||
affected.fracture()
|
||||
affected.fracture()
|
||||
|
||||
Reference in New Issue
Block a user