Merged Upstream

This is a big one.
This commit is contained in:
Vetinari
2014-03-04 22:19:09 +11:00
92 changed files with 2944 additions and 1648 deletions

View 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)

View 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

View 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

View 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

View 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

View 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]

View 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

View File

@@ -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

View File

@@ -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, \..."

View File

@@ -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())

View File

@@ -0,0 +1,102 @@
/*
Overview:
The connection_manager class stores connections in each cardinal direction on a turf.
It isn't always present if a turf has no connections, check if(connections) before using.
Contains procs for mass manipulation of connection data.
Class Vars:
NSEWUD - Connections to this turf in each cardinal direction.
Class Procs:
get(d)
Returns the connection (if any) in this direction.
Preferable to accessing the connection directly because it checks validity.
place(connection/c, d)
Called by air_master.connect(). Sets the connection in the specified direction to c.
update_all()
Called after turf/update_air_properties(). Updates the validity of all connections on this turf.
erase_all()
Called when the turf is changed with ChangeTurf(). Erases all existing connections.
check(connection/c)
Checks for connection validity. It's possible to have a reference to a connection that has been erased.
*/
/turf/var/tmp/connection_manager/connections
/connection_manager/var/connection/N
/connection_manager/var/connection/S
/connection_manager/var/connection/E
/connection_manager/var/connection/W
#ifdef ZLEVELS
/connection_manager/var/connection/U
/connection_manager/var/connection/D
#endif
/connection_manager/proc/get(d)
switch(d)
if(NORTH)
if(check(N)) return N
else return null
if(SOUTH)
if(check(S)) return S
else return null
if(EAST)
if(check(E)) return E
else return null
if(WEST)
if(check(W)) return W
else return null
#ifdef ZLEVELS
if(UP)
if(check(U)) return U
else return null
if(DOWN)
if(check(D)) return D
else return null
#endif
/connection_manager/proc/place(connection/c, d)
switch(d)
if(NORTH) N = c
if(SOUTH) S = c
if(EAST) E = c
if(WEST) W = c
#ifdef ZLEVELS
if(UP) U = c
if(DOWN) D = c
#endif
/connection_manager/proc/update_all()
if(check(N)) N.update()
if(check(S)) S.update()
if(check(E)) E.update()
if(check(W)) W.update()
#ifdef ZLEVELS
if(check(U)) U.update()
if(check(D)) D.update()
#endif
/connection_manager/proc/erase_all()
if(check(N)) N.erase()
if(check(S)) S.erase()
if(check(E)) E.erase()
if(check(W)) W.erase()
#ifdef ZLEVELS
if(check(U)) U.erase()
if(check(D)) D.erase()
#endif
/connection_manager/proc/check(connection/c)
return c && c.valid()

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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*")

View File

@@ -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 ","")

View File

@@ -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

View File

@@ -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

View File

@@ -45,7 +45,8 @@ var/datum/controller/failsafe/Failsafe
MC_defcon = 0
MC_iteration = controller_iteration
if(lighting_controller.processing)
//Lighting controller now neither processes nor iterates.
/*if(lighting_controller.processing)
if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
switch(lighting_defcon)
if(0 to 3)
@@ -60,7 +61,7 @@ var/datum/controller/failsafe/Failsafe
lighting_defcon = 0
else
lighting_defcon = 0
lighting_iteration = lighting_controller.iteration
lighting_iteration = lighting_controller.iteration*/
else
MC_defcon = 0
lighting_defcon = 0

View File

@@ -77,6 +77,7 @@ datum/controller/game_controller/proc/setup()
if(ticker)
ticker.pregame()
new/datum/controller/lighting()
lighting_controller.Initialize()

View File

@@ -19,7 +19,7 @@
feedback_add_details("admin_verb","RFailsafe")
if("Lighting")
new /datum/controller/lighting()
lighting_controller.process()
//lighting_controller.process()
feedback_add_details("admin_verb","RLighting")
if("Supply Shuttle")
supply_shuttle.process()

View File

@@ -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

View File

@@ -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,

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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

View File

@@ -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, " ", "&nbsp")]</a> " //make sure there isn't a line break in the middle of a job

View File

@@ -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")

View File

@@ -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")

View File

@@ -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()

View 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

View File

@@ -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--
/////////////////////////////////////////////

View File

@@ -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)"

View File

@@ -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)

View File

@@ -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

View File

@@ -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."

View File

@@ -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

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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")

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -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()

View File

@@ -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,

View File

@@ -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"

View File

@@ -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>"

View File

@@ -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."

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -4,7 +4,6 @@
icon_state = "ice_1"
damage = 20
flag = "energy"
embed = 1
/obj/item/projectile/forcebolt/strong
name = "force bolt"

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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/////////////////
/////////////////////////////////////////

View File

@@ -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()