Merge remote-tracking branch 'polaris/master' into polaris-sync

# Conflicts:
#	html/changelogs/.all_changelog.yml
#	maps/RandomZLevels/zresearchlabs.dmm
This commit is contained in:
Leshana
2017-04-15 23:14:32 -05:00
108 changed files with 3048 additions and 809 deletions
+2
View File
@@ -8,6 +8,8 @@
flags_inv = 0
siemens_coefficient = 0.9
action_button_name = "Toggle Head-light"
w_class = ITEMSIZE_NORMAL
ear_protection = 1
/obj/item/clothing/head/hardhat/orange
icon_state = "hardhat0_orange"
+4 -4
View File
@@ -2,7 +2,7 @@
/obj/item/clothing/head/helmet/space/skrell
name = "Skrellian helmet"
desc = "Smoothly contoured and polished to a shine. Still looks like a fishbowl."
armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100)
armor = list(melee = 20, bullet = 20, laser = 20, energy = 50, bomb = 50, bio = 100, rad = 50)
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
species_restricted = list("Skrell","Human")
@@ -15,7 +15,7 @@
/obj/item/clothing/suit/space/skrell
name = "Skrellian voidsuit"
desc = "Seems like a wetsuit with reinforced plating seamlessly attached to it. Very chic."
armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100)
armor = list(melee = 20, bullet = 20, laser = 20, energy = 50, bomb = 50, bio = 100, rad = 50)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
@@ -35,14 +35,14 @@
allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank)
phoronproof = 1
slowdown = 2
armor = list(melee = 60, bullet = 50, laser = 40,energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40,energy = 15, bomb = 30, bio = 100, rad = 50)
siemens_coefficient = 0.2
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
species_restricted = list("Vox")
/obj/item/clothing/head/helmet/space/vox
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50)
siemens_coefficient = 0.2
item_flags = STOPPRESSUREDAMAGE | THICKMATERIAL | AIRTIGHT | PHORONGUARD
flags_inv = 0
@@ -149,17 +149,19 @@
/obj/item/rig_module/chem_dispenser/ninja
interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible."
//just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face.
//Want more? Go refill. Gives the ninja another reason to have to show their face.
charges = list(
list("tricordrazine", "tricordrazine", 0, 20),
list("tramadol", "tramadol", 0, 20),
list("dexalin plus", "dexalinp", 0, 20),
list("antibiotics", "spaceacillin", 0, 20),
list("antitoxins", "anti_toxin", 0, 20),
list("nutrients", "glucose", 0, 80),
list("clotting agent", "myelamine", 0, 80),
list("hyronalin", "hyronalin", 0, 20),
list("radium", "radium", 0, 20)
list("tricordrazine", "tricordrazine", 0, 30),
list("tramadol", "tramadol", 0, 30),
list("dexalin plus", "dexalinp", 0, 30),
list("antibiotics", "spaceacillin", 0, 30),
list("antitoxins", "anti_toxin", 0, 60),
list("nutrients", "glucose", 0, 80),
list("bicaridine", "bicaridine", 0, 30),
list("clotting agent", "myelamine", 0, 30),
list("peridaxon", "peridaxon", 0, 30),
list("hyronalin", "hyronalin", 0, 30),
list("radium", "radium", 0, 30)
)
/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user)
@@ -45,7 +45,7 @@
desc = "This metal box writhes and squirms as if it were alive..."
suit_type = "alien"
icon_state = "vox_rig"
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50)
item_flags = THICKMATERIAL
siemens_coefficient = 0.2
phoronproof = 1
@@ -88,7 +88,7 @@
/obj/item/weapon/rig/vox/carapace
name = "dense alien control module"
suit_type = "dense alien"
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50)
emp_protection = 40 //change this to 30 if too high.
phoronproof = 1
@@ -109,7 +109,7 @@
name = "sinister alien control module"
suit_type = "sinister alien"
icon_state = "voxstealth_rig"
armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 30, bio = 100, rad = 100)
armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 30, bio = 100, rad = 50)
emp_protection = 40 //change this to 30 if too high.
phoronproof = 1
@@ -93,7 +93,7 @@
/obj/item/rig_module/vision,
/obj/item/rig_module/voice,
/obj/item/rig_module/fabricator/energy_net,
/obj/item/rig_module/chem_dispenser,
/obj/item/rig_module/chem_dispenser/ninja,
/obj/item/rig_module/grenade_launcher,
/obj/item/rig_module/ai_container,
/obj/item/rig_module/power_sink,
+1 -5
View File
@@ -296,11 +296,7 @@
// Handle light requirements.
if(!light_supplied)
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in current_turf
if(L)
light_supplied = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5)
else
light_supplied = 5
light_supplied = current_turf.get_lumcount() * 5
if(light_supplied)
if(abs(light_supplied - get_trait(TRAIT_IDEAL_LIGHT)) > get_trait(TRAIT_LIGHT_TOLERANCE))
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
@@ -192,7 +192,7 @@
if(growth>2 && growth == max_growth)
layer = 5
opacity = 1
set_opacity(1)
if(!isnull(seed.chems["woodpulp"]))
density = 1
else
+1 -6
View File
@@ -625,12 +625,7 @@
if(closed_system && mechanical)
light_string = "that the internal lights are set to [tray_light] lumens"
else
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
var/light_available
if(L)
light_available = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5)
else
light_available = 5
var/light_available = T.get_lumcount() * 5
light_string = "a light level of [light_available] lumens"
usr << "The tray's sensor suite is reporting [light_string] and a temperature of [environment.temperature]K at [environment.return_pressure()] kPa in the [environment_type] environment"
@@ -21,11 +21,8 @@
var/max_power = 1
/obj/item/integrated_circuit/passive/power/solar_cell/make_energy()
var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(src)
var/light_amount = 1 // Unsimulated tiles are pretend-lit, so we need to be pretend too if that somehow happens.
if(light)
light_amount = (light.lum_r + light.lum_g + light.lum_b) / 3
var/turf/T = get_turf(src)
var/light_amount = T ? T.get_lumcount() : 0
var/adjusted_power = max(max_power * light_amount, 0)
adjusted_power = round(adjusted_power, 0.1)
if(adjusted_power)
+4 -4
View File
@@ -14,9 +14,9 @@ Changes from tg DAL:
- light_range; range in tiles of the light, used for calculating falloff,
- light_power; multiplier for the brightness of lights,
- light_color; hex string representing the RGB colour of the light.
- SetLuminosity() is now set_light() and takes the three variables above.
- setLuminousity() is now set_light() and takes the three variables above.
- Variables can be left as null to not update them.
- SetOpacity() is now set_opacity().
- set_opacity() is now set_opacity().
- Areas have luminosity set to 1 permanently, no hard-lighting.
- Objects inside other objects can have lights and they properly affect the turf. (flashlights)
- area/master and area/list/related have been eviscerated since subareas aren't needed.
@@ -51,7 +51,7 @@ turf: (lighting_turf.dm)
- proc/lighting_clear_overlays():
- Delete (manual GC) all light overlays on this turf, used when changing turf to space
- proc/lighting_build_overlays():
- Create lighting overlays for this turf. Called by ChangeTurf in case the turf is being changed to use dynamic lighting.
- Create lighting overlays for this turf
atom/movable/lighting_overlay: (lighting_overlay.dm)
@@ -64,4 +64,4 @@ atom/movable/lighting_overlay: (lighting_overlay.dm)
- Change the lumcount vars and queue the overlay for update
- proc/update_overlay()
- Called by the lighting process to update the color of the overlay
*/
*/
-9
View File
@@ -1,9 +0,0 @@
#define LIGHTING_INTERVAL 5 // frequency, in 1/10ths of a second, of the lighting process
#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square
#define LIGHTING_LAMBERTIAN 1 // use lambertian shading for light sources
#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone
#define LIGHTING_ROUND_VALUE (1 / 128) //Value used to round lumcounts, values smaller than 1/255 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY.
#define LIGHTING_LAYER 10 // drawing layer for lighting overlays
#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects
-300
View File
@@ -1,300 +0,0 @@
//So much copypasta in this file, supposedly in the name of performance. If you change anything make sure to consider other places where the code may have been copied.
/datum/light_source
var/atom/top_atom
var/atom/source_atom
var/turf/source_turf
var/light_power
var/light_range
var/light_color // string, decomposed by parse_light_color()
var/lum_r
var/lum_g
var/lum_b
//hold onto the actual applied lum values so we can undo them when the lighting changes
var/tmp/applied_lum_r
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/effect_str
var/list/effect_turf
var/applied
var/vis_update //Whetever we should smartly recalculate visibility. and then only update tiles that became (in) visible to us
var/needs_update
var/destroyed
var/force_update
/datum/light_source/New(atom/owner, atom/top)
source_atom = owner
if(!source_atom.light_sources) source_atom.light_sources = list()
source_atom.light_sources += src
top_atom = top
if(top_atom != source_atom)
if(!top.light_sources) top.light_sources = list()
top_atom.light_sources += src
source_turf = top_atom
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
effect_str = list()
effect_turf = list()
update()
return ..()
/datum/light_source/proc/destroy()
destroyed = 1
force_update()
if(source_atom && source_atom.light_sources) source_atom.light_sources -= src
if(top_atom && top_atom.light_sources) top_atom.light_sources -= src
/datum/light_source/proc/update(atom/new_top_atom)
if(new_top_atom && new_top_atom != top_atom)
if(top_atom != source_atom) top_atom.light_sources -= src
top_atom = new_top_atom
if(top_atom != source_atom)
if(!top_atom.light_sources) top_atom.light_sources = list()
top_atom.light_sources += src
if(!needs_update) //Incase we're already updating either way.
lighting_update_lights += src
needs_update = 1
/datum/light_source/proc/force_update()
force_update = 1
if(!needs_update) //Incase we're already updating either way.
needs_update = 1
lighting_update_lights += src
/datum/light_source/proc/vis_update()
if(!needs_update)
needs_update = 1
lighting_update_lights += src
vis_update = 1
/datum/light_source/proc/check()
if(!source_atom || !light_range || !light_power)
destroy()
return 1
if(!top_atom)
top_atom = source_atom
. = 1
if(istype(top_atom, /turf))
if(source_turf != top_atom)
source_turf = top_atom
. = 1
else if(top_atom.loc != source_turf)
source_turf = top_atom.loc
. = 1
if(source_atom.light_power != light_power)
light_power = source_atom.light_power
. = 1
if(source_atom.light_range != light_range)
light_range = source_atom.light_range
. = 1
if(light_range && light_power && !applied)
. = 1
if(source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
. = 1
/datum/light_source/proc/parse_light_color()
if(light_color)
lum_r = GetRedPart(light_color) / 255
lum_g = GetGreenPart(light_color) / 255
lum_b = GetBluePart(light_color) / 255
else
lum_r = 1
lum_g = 1
lum_b = 1
#if LIGHTING_FALLOFF == 1 //circular
#define LUM_DISTANCE(swapvar, O, T) swapvar = (O.x - T.x)**2 + (O.y - T.y)**2 + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(sqrt(swapvar) / max(1,light_range))) * (1 / sqrt(swapvar + 1)))
#else
#define LUM_ATTENUATION(swapvar) swapvar = 1 - CLAMP01(sqrt(swapvar) / max(1,light_range))
#endif
#elif LIGHTING_FALLOFF == 2 //square
#define LUM_DISTANCE(swapvar, O, T) swapvar = abs(O.x - T.x) + abs(O.y - T.y) + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(swapvar / max(1,light_range))) * (1 / sqrt(swapvar**2 + 1)))
#else
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01(swapvar / max(1,light_range))
#endif
#endif
#define LUM_FALLOFF(swapvar, O, T) \
LUM_DISTANCE(swapvar, O, T); \
LUM_ATTENUATION(swapvar);
/datum/light_source/proc/apply_lum()
applied = 1
//Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
if(istype(source_turf))
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
if(T.lighting_overlay)
var/strength
LUM_FALLOFF(strength, T, source_turf)
strength *= light_power
if(!strength) //Don't add turfs that aren't affected to the affected turfs.
continue
strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
effect_str += strength
T.lighting_overlay.update_lumcount(
applied_lum_r * strength,
applied_lum_g * strength,
applied_lum_b * strength
)
else
effect_str += 0
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
effect_turf += T
END_FOR_DVIEW
/datum/light_source/proc/remove_lum()
applied = 0
var/i = 1
for(var/turf/T in effect_turf)
if(T.affecting_lights)
T.affecting_lights -= src
if(T.lighting_overlay)
var/str = effect_str[i]
T.lighting_overlay.update_lumcount(
-str * applied_lum_r,
-str * applied_lum_g,
-str * applied_lum_b
)
i++
effect_str.Cut()
effect_turf.Cut()
//Smartly updates the lighting, only removes lum from and adds lum to turfs that actually got changed.
//This is for lights that need to reconsider due to nearby opacity changes.
//Stupid dumb copy pasta because BYOND and speed.
/datum/light_source/proc/smart_vis_update()
var/list/view[0]
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
view += T //Filter out turfs.
END_FOR_DVIEW
//This is the part where we calculate new turfs (if any)
var/list/new_turfs = view - effect_turf //This will result with all the tiles that are added.
for(var/turf/T in new_turfs)
if(T.lighting_overlay)
LUM_FALLOFF(., T, source_turf)
. *= light_power
if(!.) //Don't add turfs that aren't affected to the affected turfs.
continue
. = round(., LIGHTING_ROUND_VALUE)
effect_str += .
T.lighting_overlay.update_lumcount(
applied_lum_r * .,
applied_lum_g * .,
applied_lum_b * .
)
else
effect_str += 0
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
effect_turf += T
var/list/old_turfs = effect_turf - view
for(var/turf/T in old_turfs)
//Insert not-so-huge copy paste from remove_lum().
var/idx = effect_turf.Find(T) //Get the index, luckily Find() is cheap in small lists like this. (with small I mean under a couple thousand len)
if(T.affecting_lights)
T.affecting_lights -= src
if(T.lighting_overlay)
var/str = effect_str[idx]
T.lighting_overlay.update_lumcount(-str * applied_lum_r, -str * applied_lum_g, -str * applied_lum_b)
effect_turf.Cut(idx, idx + 1)
effect_str.Cut(idx, idx + 1)
//Whoop yet not another copy pasta because speed ~~~~BYOND.
//Calculates and applies lighting for a single turf. This is intended for when a turf switches to
//using dynamic lighting when it was not doing so previously (when constructing a floor on space, for example).
//Assumes the turf is visible and such.
//For the love of god don't call this proc when it's not needed! Lighting artifacts WILL happen!
/datum/light_source/proc/calc_turf(var/turf/T)
var/idx = effect_turf.Find(T)
if(!idx)
return //WHY.
if(T.lighting_overlay)
#if LIGHTING_FALLOFF == 1 // circular
. = (T.lighting_overlay.x - source_turf.x)**2 + (T.lighting_overlay.y - source_turf.y)**2 + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
. = CLAMP01((1 - CLAMP01(sqrt(.) / light_range)) * (1 / (sqrt(. + 1))))
#else
. = 1 - CLAMP01(sqrt(.) / light_range)
#endif
#elif LIGHTING_FALLOFF == 2 // square
. = abs(T.lighting_overlay.x - source_turf.x) + abs(T.lighting_overlay.y - source_turf.y) + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
. = CLAMP01((1 - CLAMP01(. / light_range)) * (1 / (sqrt(.)**2 + )))
#else
. = 1 - CLAMP01(. / light_range)
#endif
#endif
. *= light_power
. = round(., LIGHTING_ROUND_VALUE)
effect_str[idx] = .
//Since the applied_lum values are what are (later) removed by remove_lum.
//Anything we apply to the lighting overlays HAS to match what remove_lum uses.
T.lighting_overlay.update_lumcount(
applied_lum_r * .,
applied_lum_g * .,
applied_lum_b * .
)
#undef LUM_FALLOFF
#undef LUM_DISTANCE
#undef LUM_ATTENUATION
+9
View File
@@ -0,0 +1,9 @@
/area
luminosity = TRUE
var/dynamic_lighting = TRUE
/area/New()
. = ..()
if(dynamic_lighting)
luminosity = FALSE
+38 -11
View File
@@ -1,12 +1,14 @@
/atom
var/light_power = 1 // intensity of the light
var/light_range = 0 // range in tiles of the light
var/light_color // RGB string representing the colour of the light
var/light_color // Hexadecimal RGB string representing the colour of the light
var/datum/light_source/light
var/list/light_sources
/atom/proc/set_light(l_range, l_power, l_color)
// Nonsensical value for l_color default, so we can detect if it gets set to null.
#define NONSENSICAL_VALUE -99999
/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE)
. = 0 //make it less costly if nothing's changed
if(l_power != null && l_power != light_power)
@@ -15,16 +17,17 @@
if(l_range != null && l_range != light_range)
light_range = l_range
. = 1
if(l_color != null && l_color != light_color)
if(l_color != NONSENSICAL_VALUE && l_color != light_color)
light_color = l_color
. = 1
if(.) update_light()
/atom/proc/copy_light(atom/A)
set_light(A.light_range, A.light_power, A.light_color)
#undef NONSENSICAL_VALUE
/atom/proc/update_light()
set waitfor = FALSE
if(!light_power || !light_range)
if(light)
light.destroy()
@@ -42,9 +45,14 @@
/atom/New()
. = ..()
if(light_power && light_range)
update_light()
if(opacity && isturf(loc))
var/turf/T = loc
T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways.
/atom/Destroy()
if(light)
light.destroy()
@@ -57,19 +65,38 @@
T.reconsider_lights()
return ..()
/atom/Entered(atom/movable/obj, atom/prev_loc)
/atom/movable/Move()
var/turf/old_loc = loc
. = ..()
if(obj && prev_loc != src)
for(var/datum/light_source/L in obj.light_sources)
if(loc != old_loc)
for(var/datum/light_source/L in light_sources)
L.source_atom.update_light()
var/turf/new_loc = loc
if(istype(old_loc) && opacity)
old_loc.reconsider_lights()
if(istype(new_loc) && opacity)
new_loc.reconsider_lights()
/atom/proc/set_opacity(new_opacity)
var/old_opacity = opacity
if(new_opacity == opacity)
return
opacity = new_opacity
var/turf/T = loc
if(old_opacity != new_opacity && istype(T))
var/turf/T = isturf(src) ? src : loc
if(!isturf(T))
return
if(new_opacity == TRUE)
T.has_opaque_atom = TRUE
T.reconsider_lights()
else
var/old_has_opaque_atom = T.has_opaque_atom
T.recalc_atom_opacity()
if(old_has_opaque_atom != T.has_opaque_atom)
T.reconsider_lights()
/obj/item/equipped()
. = ..()
+133
View File
@@ -0,0 +1,133 @@
/var/total_lighting_corners = 0
/var/datum/lighting_corner/dummy/dummy_lighting_corner = new
// Because we can control each corner of every lighting overlay.
// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.
// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list.
/var/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)
/datum/lighting_corner
var/list/turf/masters = list()
var/list/datum/light_source/affecting = list() // Light sources affecting us.
var/active = FALSE // TRUE if one of our masters has dynamic lighting.
var/x = 0
var/y = 0
var/z = 0
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
var/needs_update = FALSE
var/cache_r = LIGHTING_SOFT_THRESHOLD
var/cache_g = LIGHTING_SOFT_THRESHOLD
var/cache_b = LIGHTING_SOFT_THRESHOLD
var/cache_mx = 0
var/update_gen = 0
/datum/lighting_corner/New(var/turf/new_turf, var/diagonal)
. = ..()
total_lighting_corners++
masters[new_turf] = turn(diagonal, 180)
z = new_turf.z
var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one.
x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5)
y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5)
// My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal).
// Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
// So we'll have this hardcode instead.
var/turf/T
var/i
// Diagonal one is easy.
T = get_step(new_turf, diagonal)
if (T) // In case we're on the map's border.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = diagonal
i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
T.corners[i] = src
// Now the horizontal one.
T = get_step(new_turf, horizontal)
if (T) // Ditto.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
// And finally the vertical one.
T = get_step(new_turf, vertical)
if (T)
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
update_active()
/datum/lighting_corner/proc/update_active()
active = FALSE
for (var/turf/T in masters)
if (T.lighting_overlay)
active = TRUE
// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b)
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if (!needs_update)
needs_update = TRUE
lighting_update_corners += src
/datum/lighting_corner/proc/update_overlays()
// Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually.
var/lum_r = src.lum_r
var/lum_g = src.lum_g
var/lum_b = src.lum_b
var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1.
. = 1 // factor
if (mx > 1)
. = 1 / mx
#if LIGHTING_SOFT_THRESHOLD != 0
else if (mx < LIGHTING_SOFT_THRESHOLD)
. = 0 // 0 means soft lighting.
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
#else
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE)
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE)
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE)
#endif
cache_mx = round(mx, LIGHTING_ROUND_VALUE)
for (var/TT in masters)
var/turf/T = TT
if (T.lighting_overlay)
if (!T.lighting_overlay.needs_update)
T.lighting_overlay.needs_update = TRUE
lighting_update_overlays += T.lighting_overlay
/datum/lighting_corner/dummy/New()
return
+93 -70
View File
@@ -1,102 +1,125 @@
/var/total_lighting_overlays = 0
/atom/movable/lighting_overlay
name = ""
mouse_opacity = 0
simulated = 0
anchored = 1
icon = LIGHTING_ICON
icon_state = "light1"
layer = LIGHTING_LAYER
invisibility = INVISIBILITY_LIGHTING
color = "#000000"
color = LIGHTING_BASE_MATRIX
icon_state = "light1"
auto_init = 0 // doesn't need special init
blend_mode = BLEND_MULTIPLY
var/lum_r
var/lum_g
var/lum_b
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
var/needs_update
var/needs_update = FALSE
/atom/movable/lighting_overlay/New()
/atom/movable/lighting_overlay/New(var/atom/loc, var/no_update = FALSE)
. = ..()
verbs.Cut()
total_lighting_overlays++
var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays in things that aren't turfs.
var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays outside of turfs.
T.lighting_overlay = src
T.luminosity = 0
/atom/movable/lighting_overlay/proc/update_lumcount(delta_r, delta_g, delta_b)
if(!delta_r && !delta_g && !delta_b) //Nothing is being changed all together.
if(no_update)
return
var/should_update = 0
if(!needs_update) //If this isn't true, we're already updating anyways.
if(max(lum_r, lum_g, lum_b) < 1) //Any change that could happen WILL change appearance.
should_update = 1
else if(max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b) < 1) //The change would bring us under 1 max lum, again, guaranteed to change appearance.
should_update = 1
else //We need to make sure that the colour ratios won't change in this code block.
var/mx1 = max(lum_r, lum_g, lum_b)
var/mx2 = max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b)
if(lum_r / mx1 != (lum_r + delta_r) / mx2 || lum_g / mx1 != (lum_g + delta_g) / mx2 || lum_b / mx1 != (lum_b + delta_b) / mx2) //Stuff would change.
should_update = 1
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if(!needs_update && should_update)
needs_update = 1
lighting_update_overlays += src
update_overlay()
/atom/movable/lighting_overlay/proc/update_overlay()
set waitfor = FALSE
var/turf/T = loc
if(istype(T)) //Incase we're not on a turf, pool ourselves, something happened.
if(lum_r == lum_g && lum_r == lum_b) //greyscale
blend_mode = BLEND_OVERLAY
if(lum_r <= 0)
T.luminosity = 0
color = "#000000"
alpha = 255
else
T.luminosity = 1
color = "#000000"
alpha = (1 - min(lum_r, 1)) * 255
if(!istype(T))
if(loc)
log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!")
else
alpha = 255
var/mx = max(lum_r, lum_g, lum_b)
. = 1 // factor
if(mx > 1)
. = 1/mx
blend_mode = BLEND_MULTIPLY
color = rgb(lum_r * 255 * ., lum_g * 255 * ., lum_b * 255 * .)
if(color != "#000000")
T.luminosity = 1
else //No light, set the turf's luminosity to 0 to remove it from view()
T.luminosity = 0
else
warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : ""]) in update_overlay() and got pooled!")
log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!")
qdel(src)
return
/atom/movable/lighting_overlay/ResetVars()
loc = null
// To the future coder who sees this and thinks
// "Why didn't he just use a loop?"
// Well my man, it's because the loop performed like shit.
// And there's no way to improve it because
// without a loop you can make the list all at once which is the fastest you're gonna get.
// Oh it's also shorter line wise.
// Including with these comments.
lum_r = 0
lum_g = 0
lum_b = 0
// See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are.
// No I seriously cannot think of a more efficient method, fuck off Comic.
var/datum/lighting_corner/cr = T.corners[3] || dummy_lighting_corner
var/datum/lighting_corner/cg = T.corners[2] || dummy_lighting_corner
var/datum/lighting_corner/cb = T.corners[4] || dummy_lighting_corner
var/datum/lighting_corner/ca = T.corners[1] || dummy_lighting_corner
color = "#000000"
var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
needs_update = 0
var/rr = cr.cache_r
var/rg = cr.cache_g
var/rb = cr.cache_b
var/gr = cg.cache_r
var/gg = cg.cache_g
var/gb = cg.cache_b
var/br = cb.cache_r
var/bg = cb.cache_g
var/bb = cb.cache_b
var/ar = ca.cache_r
var/ag = ca.cache_g
var/ab = ca.cache_b
#if LIGHTING_SOFT_THRESHOLD != 0
var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD
#else
// Because of floating points, it won't even be a flat 0.
// This number is mostly arbitrary.
var/set_luminosity = max > 1e-6
#endif
if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8))
//anything that passes the first case is very likely to pass the second, and addition is a little faster in this case
icon_state = "transparent"
color = null
else if(!set_luminosity)
icon_state = LIGHTING_ICON_STATE_DARK
color = null
else
icon_state = null
color = list(
rr, rg, rb, 00,
gr, gg, gb, 00,
br, bg, bb, 00,
ar, ag, ab, 00,
00, 00, 00, 01
)
luminosity = set_luminosity
// Variety of overrides so the overlays don't get affected by weird things.
/atom/movable/lighting_overlay/ex_act()
return
/atom/movable/lighting_overlay/singularity_act()
return
/atom/movable/lighting_overlay/singularity_pull()
return
/atom/movable/lighting_overlay/Destroy()
lighting_update_overlays -= src
total_lighting_overlays--
global.lighting_update_overlays -= src
global.lighting_update_overlays_old -= src
var/turf/T = loc
if(istype(T))
T.lighting_overlay = null
..()
T.luminosity = 1
return ..()
-35
View File
@@ -1,35 +0,0 @@
/datum/controller/process/lighting/setup()
name = "lighting"
schedule_interval = LIGHTING_INTERVAL
create_lighting_overlays()
/datum/controller/process/lighting/doWork()
var/list/lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated.
lighting_update_lights = null //Nulling it first because of http://www.byond.com/forum/?post=1854520
lighting_update_lights = list()
for(var/datum/light_source/L in lighting_update_lights_old)
if(L.destroyed || L.check() || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
L.vis_update = 0
L.force_update = 0
L.needs_update = 0
SCHECK
var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = null //Same as above
lighting_update_overlays = list()
for(var/atom/movable/lighting_overlay/O in lighting_update_overlays_old)
O.update_overlay()
O.needs_update = 0
SCHECK
+16
View File
@@ -0,0 +1,16 @@
/proc/create_all_lighting_overlays()
for(var/zlevel = 1 to world.maxz)
create_lighting_overlays_zlevel(zlevel)
/proc/create_lighting_overlays_zlevel(var/zlevel)
ASSERT(zlevel)
for(var/turf/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
if(!T.dynamic_lighting)
continue
var/area/A = T.loc
if(!A.dynamic_lighting)
continue
new /atom/movable/lighting_overlay(T, TRUE)
+290
View File
@@ -0,0 +1,290 @@
/var/total_lighting_sources = 0
// This is where the fun begins.
// These are the main datums that emit light.
/datum/light_source
var/atom/top_atom // The atom we're emitting light from(for example a mob if we're from a flashlight that's being held).
var/atom/source_atom // The atom that we belong to.
var/turf/source_turf // The turf under the above.
var/light_power // Intensity of the emitter light.
var/light_range // The range of the emitted light.
var/light_color // The colour of the light, string, decomposed by parse_light_color()
// Variables for keeping track of the colour.
var/lum_r
var/lum_g
var/lum_b
// The lumcount values used to apply the light.
var/tmp/applied_lum_r
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
var/list/turf/affecting_turfs
var/applied = FALSE // Whether we have applied our light yet or not.
var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became(in)visible to us.
var/needs_update // Whether we are queued for an update.
var/destroyed // Whether we are destroyed and need to stop emitting light.
var/force_update
/datum/light_source/New(var/atom/owner, var/atom/top)
total_lighting_sources++
source_atom = owner // Set our new owner.
if(!source_atom.light_sources)
source_atom.light_sources = list()
source_atom.light_sources += src // Add us to the lights of our owner.
top_atom = top
if(top_atom != source_atom)
if(!top.light_sources)
top.light_sources = list()
top_atom.light_sources += src
source_turf = top_atom
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
effect_str = list()
affecting_turfs = list()
update()
return ..()
// Kill ourselves.
/datum/light_source/proc/destroy()
total_lighting_sources--
destroyed = TRUE
force_update()
if(source_atom)
if(!source_atom.light_sources)
log_runtime(EXCEPTION("Atom [source_atom] was a light source, but lacked a light source list!\n"), source_atom)
else
source_atom.light_sources -= src
if(top_atom)
top_atom.light_sources -= src
// Call it dirty, I don't care.
// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates.
// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a() macro that has no arguments :|.
#define effect_update(BYOND) \
if(!needs_update) \
{ \
lighting_update_lights += src; \
needs_update = TRUE; \
}
// This proc will cause the light source to update the top atom, and add itself to the update queue.
/datum/light_source/proc/update(var/atom/new_top_atom)
// This top atom is different.
if(new_top_atom && new_top_atom != top_atom)
if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom.
top_atom.light_sources -= src
top_atom = new_top_atom
if(top_atom != source_atom)
if(!top_atom.light_sources)
top_atom.light_sources = list()
top_atom.light_sources += src // Add ourselves to the light sources of our new top atom.
effect_update(null)
// Will force an update without checking if it's actually needed.
/datum/light_source/proc/force_update()
force_update = 1
effect_update(null)
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
/datum/light_source/proc/vis_update()
vis_update = 1
effect_update(null)
// Will check if we actually need to update, and update any variables that may need to be updated.
/datum/light_source/proc/check()
if(!source_atom || !light_range || !light_power)
destroy()
return 1
if(!top_atom)
top_atom = source_atom
. = 1
if(isturf(top_atom))
if(source_turf != top_atom)
source_turf = top_atom
. = 1
else if(top_atom.loc != source_turf)
source_turf = top_atom.loc
. = 1
if(source_atom.light_power != light_power)
light_power = source_atom.light_power
. = 1
if(source_atom.light_range != light_range)
light_range = source_atom.light_range
. = 1
if(light_range && light_power && !applied)
. = 1
if(source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
. = 1
// Decompile the hexadecimal colour into lumcounts of each perspective.
/datum/light_source/proc/parse_light_color()
if(light_color)
lum_r = GetRedPart (light_color) / 255
lum_g = GetGreenPart(light_color) / 255
lum_b = GetBluePart (light_color) / 255
else
lum_r = 1
lum_g = 1
lum_b = 1
// Macro that applies light to a new corner.
// It is a macro in the interest of speed, yet not having to copy paste it.
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
// As such this all gets counted as a single line.
// The braces and semicolons are there to be able to do this on a single line.
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, source_turf); \
\
. *= light_power; \
\
effect_str[C] = .; \
\
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// I don't need to explain what this does, do I?
#define REMOVE_CORNER(C) \
. = -effect_str[C]; \
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// This is the define used to calculate falloff.
#define LUM_FALLOFF(C, T)(1 - CLAMP01(sqrt((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
/datum/light_source/proc/apply_lum()
var/static/update_gen = 1
applied = 1
// Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
for(var/datum/lighting_corner/C in T.get_corners())
if(C.update_gen == update_gen)
continue
C.update_gen = update_gen
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
affecting_turfs += T
update_gen++
/datum/light_source/proc/remove_lum()
applied = FALSE
for(var/turf/T in affecting_turfs)
if(!T.affecting_lights)
T.affecting_lights = list()
else
T.affecting_lights -= src
affecting_turfs.Cut()
for(var/datum/lighting_corner/C in effect_str)
REMOVE_CORNER(C)
C.affecting -= src
effect_str.Cut()
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
if(effect_str.Find(C)) // Already have one.
REMOVE_CORNER(C)
APPLY_CORNER(C)
/datum/light_source/proc/smart_vis_update()
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
FOR_DVIEW(var/turf/T, light_range, source_turf, 0)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
corners |= T.get_corners()
turfs += T
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
affecting_turfs += L
for(var/turf/T in L)
if(!T.affecting_lights)
T.affecting_lights = list(src)
else
T.affecting_lights += src
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
affecting_turfs -= L
for(var/turf/T in L)
T.affecting_lights -= src
for(var/datum/lighting_corner/C in corners - effect_str) // New corners
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
for(var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
REMOVE_CORNER(C)
C.affecting -= src
effect_str -= C
#undef effect_update
#undef LUM_FALLOFF
#undef REMOVE_CORNER
#undef APPLY_CORNER
-25
View File
@@ -1,25 +0,0 @@
/var/list/lighting_update_lights = list()
/var/list/lighting_update_overlays = list()
/area/var/lighting_use_dynamic = 1
// duplicates lots of code, but this proc needs to be as fast as possible.
/proc/create_lighting_overlays(zlevel = 0)
var/area/A
if(zlevel == 0) // populate all zlevels
for(var/turf/T in world)
if(T.dynamic_lighting)
A = T.loc
if(A.lighting_use_dynamic)
var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
T.lighting_overlay = O
else
for(var/x = 1; x <= world.maxx; x++)
for(var/y = 1; y <= world.maxy; y++)
var/turf/T = locate(x, y, zlevel)
if(T.dynamic_lighting)
A = T.loc
if(A.lighting_use_dynamic)
var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
T.lighting_overlay = O
+85 -15
View File
@@ -1,32 +1,102 @@
/turf
var/list/affecting_lights
var/atom/movable/lighting_overlay/lighting_overlay
var/dynamic_lighting = TRUE // Does the turf use dynamic lighting?
luminosity = 1
var/tmp/lighting_corners_initialised = FALSE
var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf.
var/tmp/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay.
var/tmp/list/datum/lighting_corner/corners
var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.
/turf/New()
. = ..()
if(opacity)
has_opaque_atom = TRUE
// Causes any affecting light sources to be queued for a visibility update, for example a door got opened.
/turf/proc/reconsider_lights()
for(var/datum/light_source/L in affecting_lights)
L.vis_update()
/turf/proc/lighting_clear_overlays()
/turf/proc/lighting_clear_overlay()
if(lighting_overlay)
qdel(lighting_overlay)
/turf/proc/lighting_build_overlays()
for(var/datum/lighting_corner/C in corners)
C.update_active()
// Builds a lighting overlay for us, but only if our area is dynamic.
/turf/proc/lighting_build_overlay()
if(lighting_overlay)
return
var/area/A = loc
if(A.dynamic_lighting)
if(!lighting_corners_initialised)
generate_missing_corners()
new /atom/movable/lighting_overlay(src)
for(var/datum/lighting_corner/C in corners)
if(!C.active) // We would activate the corner, calculate the lighting for it.
for(var/L in C.affecting)
var/datum/light_source/S = L
S.recalc_corner(C)
C.active = TRUE
// Used to get a scaled lumcount.
/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
if(!lighting_overlay)
var/area/A = loc
if(A.lighting_use_dynamic)
var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, src)
lighting_overlay = O
return 1
//Make the light sources recalculate us so the lighting overlay updates immediately
for(var/datum/light_source/L in affecting_lights)
L.calc_turf(src)
var/totallums = 0
for(var/datum/lighting_corner/L in corners)
totallums += max(L.lum_r, L.lum_g, L.lum_b)
/turf/Entered(atom/movable/obj)
totallums /= 4 // 4 corners, max channel selected, return the average
totallums =(totallums - minlum) /(maxlum - minlum)
return CLAMP01(totallums)
// Can't think of a good name, this proc will recalculate the has_opaque_atom variable.
/turf/proc/recalc_atom_opacity()
has_opaque_atom = FALSE
for(var/atom/A in src.contents + src) // Loop through every movable atom on our tile PLUS ourselves (we matter too...)
if(A.opacity)
has_opaque_atom = TRUE
// If an opaque movable atom moves around we need to potentially update visibility.
/turf/Entered(var/atom/movable/Obj, var/atom/OldLoc)
. = ..()
if(obj && obj.opacity)
if(Obj && Obj.opacity)
has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
reconsider_lights()
/turf/Exited(atom/movable/obj)
/turf/Exited(var/atom/movable/Obj, var/atom/newloc)
. = ..()
if(obj && obj.opacity)
if(Obj && Obj.opacity)
recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates.
reconsider_lights()
/turf/proc/get_corners()
if(has_opaque_atom)
return null // Since this proc gets used in a for loop, null won't be looped though.
return corners
/turf/proc/generate_missing_corners()
lighting_corners_initialised = TRUE
if(!corners)
corners = list(null, null, null, null)
for(var/i = 1 to 4)
if(corners[i]) // Already have a corner on this direction.
continue
corners[i] = new /datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i])
+2 -4
View File
@@ -1,9 +1,7 @@
#undef LIGHTING_INTERVAL
#undef LIGHTING_FALLOFF
#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
#undef LIGHTING_RESOLUTION
#undef LIGHTING_LAYER
#undef LIGHTING_ICON
#undef LIGHTING_BASE_MATRIX
@@ -96,6 +96,7 @@
recipes += new/datum/stack_recipe("knife grip", /obj/item/weapon/material/butterflyhandle, 4, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]")
recipes += new/datum/stack_recipe("dark floor tile", /obj/item/stack/tile/floor_dark, 1, 4, 20)
recipes += new/datum/stack_recipe("roller bed", /obj/item/roller, 5, time = 30, on_floor = 1)
recipes += new/datum/stack_recipe("whetstone", /obj/item/weapon/whetstone, 2, time = 10)
/material/sandstone/generate_recipes()
..()
+1
View File
@@ -82,6 +82,7 @@ var/list/mining_overlay_cache = list()
/turf/simulated/mineral/proc/update_general()
update_icon(1)
if(ticker && ticker.current_state == GAME_STATE_PLAYING)
recalc_atom_opacity()
reconsider_lights()
if(air_master)
air_master.mark_for_update(src)
@@ -4,12 +4,7 @@
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(loc)) //else, there's considered to be no light
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = 2 * (min(5,L.lum_r + L.lum_g + L.lum_b) - 2.5) //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 5
light_amount = T.get_lumcount() * 5
nutrition += light_amount
@@ -706,7 +706,7 @@
if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses))
if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/sechud/aviator))
var/obj/item/clothing/glasses/sunglasses/sechud/aviator/S = src.glasses
if(!S.active)
if(!S.on)
number += 1
else if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/medhud/aviator))
number += 0
@@ -235,8 +235,6 @@
nutrition -= 200
// Theoretically the only internal organ a slime will have
// is the slime core. but we might as well be thorough.
for(var/obj/item/organ/I in internal_organs)
if(I.damage > 0)
I.damage = 0
@@ -245,6 +243,7 @@
// Replace completely missing limbs.
for(var/limb_type in src.species.has_limbs)
var/obj/item/organ/external/E = src.organs_by_name[limb_type]
E.disfigured = 0
if(E && (E.is_stump() || (E.status & (ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_MUTATED))))
E.removed()
qdel(E)
+5 -14
View File
@@ -817,11 +817,7 @@
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(loc)) //else, there's considered to be no light
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = min(10,L.lum_r + L.lum_g + L.lum_b) - 2 //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 10
light_amount = T.get_lumcount() * 10
nutrition += light_amount
traumatic_shock -= light_amount
@@ -840,11 +836,7 @@
var/light_amount = 0
if(isturf(loc))
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = L.lum_r + L.lum_g + L.lum_b //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 10
light_amount = T.get_lumcount() * 10
if(light_amount > species.light_dam) //if there's enough light, start dying
take_overall_damage(1,1)
else //heal in the dark
@@ -921,9 +913,9 @@
//Brain damage from Oxyloss
if(should_have_organ("brain"))
var/brainOxPercent = 0.02 //Default2% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage
var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage
if(CE_STABLE in chem_effects)
brainOxPercent = 0.01 //Halved in effect
brainOxPercent = 0.008 //Halved in effect
if(oxyloss >= 20 && prob(5))
adjustBrainLoss(brainOxPercent * oxyloss)
@@ -1364,8 +1356,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/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L && L.lum_r + L.lum_g + L.lum_b == 0)
if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD)
playsound_local(src,pick(scarySounds),50, 1, -1)
/mob/living/carbon/human/handle_stomach()
+2 -12
View File
@@ -75,14 +75,10 @@
if(affecting)
if(affecting.buckled)
return null
if(!affecting.Adjacent(affecting.grabbed_by))
qdel(src)
return null
if(state >= GRAB_AGGRESSIVE)
animate(affecting, pixel_x = 0, pixel_y = 0, 4, 1)
. = affecting
qdel(src)
return
return affecting
return null
@@ -299,9 +295,6 @@
return
if(world.time < (last_action + 20))
return
if(!M.Adjacent(user))
qdel(src)
return
last_action = world.time
reset_kill_state() //using special grab moves will interrupt choking them
@@ -347,9 +340,6 @@
qdel(src)
/obj/item/weapon/grab/proc/reset_kill_state()
if(!assailant)
qdel(src)
return
if(state == GRAB_KILL)
assailant.visible_message("<span class='warning'>[assailant] lost \his tight grip on [affecting]'s neck!</span>")
hud.icon_state = "kill"
@@ -388,7 +388,7 @@
balding
name = "Balding Hair"
icon_state = "hair_e"
gender = MALE // turnoff!
gender = MALE
familyman
name = "The Family Man"
@@ -529,9 +529,19 @@
icon_state = "hair_coffeehouse"
gender = MALE
undercut
undercut1
name = "Undercut"
icon_state = "hair_undercut"
icon_state = "hair_undercut1"
gender = MALE
undercut2
name = "Undercut Swept Right"
icon_state = "hair_undercut2"
gender = MALE
undercut3
name = "Undercut Swept Left"
icon_state = "hair_undercut3"
gender = MALE
partfade
@@ -551,7 +561,7 @@
/datum/sprite_accessory/facial_hair
icon = 'icons/mob/Human_face.dmi'
gender = MALE // barf (unless you're a dorf, dorfs dig chix /w beards :P)
gender = MALE
shaved
name = "Shaved"
+1
View File
@@ -203,6 +203,7 @@ var/list/organ_cache = list()
/obj/item/organ/proc/rejuvenate(var/ignore_prosthetic_prefs)
damage = 0
status = 0
germ_level = 0
if(!ignore_prosthetic_prefs && owner && owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name)
var/status = owner.client.prefs.organ_data[organ_tag]
if(status == "assisted")
@@ -163,6 +163,7 @@ obj/aiming_overlay/proc/update_aiming_deferred()
owner.visible_message("<span class='danger'>\The [owner] turns \the [thing] on \the [target]!</span>")
else
owner.visible_message("<span class='danger'>\The [owner] aims \the [thing] at \the [target]!</span>")
log_and_message_admins("aimed \a [thing] at [key_name(target)].")
if(owner.client)
owner.client.add_gun_icons()
@@ -70,11 +70,11 @@
// Create a decorative ramp bottom and flatten out our current ramp.
density = 0
opacity = 0
set_opacity(0)
icon_state = "ramptop"
var/obj/structure/droppod_door/door_bottom = new(T)
door_bottom.deployed = 1
door_bottom.density = 0
door_bottom.opacity = 0
door_bottom.set_opacity(0)
door_bottom.dir = src.dir
door_bottom.icon_state = "rampbottom"
@@ -134,7 +134,7 @@
if(rag)
var/underlay_image = image(icon='icons/obj/drinks.dmi', icon_state=rag.on_fire? "[rag_underlay]_lit" : rag_underlay)
underlays += underlay_image
copy_light(rag)
set_light(rag.light_range, rag.light_power, rag.light_color)
else
set_light(0)
+6 -6
View File
@@ -54,8 +54,8 @@
playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1)
check_failure()
opacity = 1
spawn(20) if(src) opacity = 0
set_opacity(1)
spawn(20) if(!deleted(src)) set_opacity(0)
..()
@@ -63,8 +63,8 @@
health -= Proj.get_structure_damage()
..()
check_failure()
opacity = 1
spawn(20) if(src) opacity = 0
set_opacity(1)
spawn(20) if(!deleted(src)) set_opacity(0)
/obj/machinery/shield/ex_act(severity)
switch(severity)
@@ -113,8 +113,8 @@
check_failure()
//The shield becomes dense to absorb the blow.. purely asthetic.
opacity = 1
spawn(20) if(src) opacity = 0
set_opacity(1)
spawn(20) if(!deleted(src)) set_opacity(0)
..()
return
+113 -33
View File
@@ -1,61 +1,100 @@
//---------- actual energy field
// Each field object has a strength var (mensured in "Renwicks").
// Melee weapons do 5% of their normal (force var) damage, so a harmbaton would do 0.75 Renwick.
// Projectiles do 5% of their structural damage, so a normal laser would do 2 Renwick damage.
// For meteors, one Renwick is about equal to one layer of r-wall.
// Meteors will be completely halted by the shield if the shield survives the impact.
// Explosions do 4 Renwick of damage per severity, at a max of 12.
/obj/effect/energy_field
name = "energy field"
name = "energy shield field"
desc = "Impenetrable field of energy, capable of blocking anything as long as it's active."
icon = 'icons/obj/machines/shielding.dmi'
icon_state = "shieldsparkles"
icon_state = "shield"
alpha = 100
anchored = 1
layer = 4.1 //just above mobs
density = 0
invisibility = 101
var/strength = 0
var/obj/machinery/shield_gen/my_gen = null
var/strength = 0 // in Renwicks
var/ticks_recovering = 10
var/max_strength = 10
/obj/effect/energy_field/New()
..()
/obj/effect/energy_field/New(var/newloc, var/new_gen)
..(newloc)
my_gen = new_gen
update_nearby_tiles()
/obj/effect/energy_field/Destroy()
update_nearby_tiles()
my_gen.field.Remove(src)
my_gen = null
var/turf/current_loc = get_turf(src)
spawn(1) // Updates neightbors after we're gone.
for(var/direction in cardinal)
var/turf/T = get_step(current_loc, direction)
if(T)
for(var/obj/effect/energy_field/F in T)
F.update_icon()
..()
/obj/effect/energy_field/ex_act(var/severity)
Stress(0.5 + severity)
adjust_strength(-(4 - severity) * 4)
/obj/effect/energy_field/bullet_act(var/obj/item/projectile/Proj)
Stress(Proj.get_structure_damage() / 10)
adjust_strength(-Proj.get_structure_damage() / 10)
/obj/effect/energy_field/proc/Stress(var/severity)
strength -= severity
/obj/effect/energy_field/attackby(obj/item/W, mob/user)
if(W.force)
adjust_strength(-W.force / 20)
user.do_attack_animation(src)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
..()
//if we take too much damage, drop out - the generator will bring us back up if we have enough power
ticks_recovering = min(ticks_recovering + 2, 10)
if(strength < 1)
invisibility = 101
density = 0
ticks_recovering = 10
strength = 0
else if(strength >= 1)
invisibility = 0
density = 1
/obj/effect/energy_field/attack_hand(var/mob/living/user)
impact_effect(3) // Harmless, but still produces the 'impact' effect.
..()
/obj/effect/energy_field/proc/Strengthen(var/severity)
strength += severity
if (strength < 0)
strength = 0
/obj/effect/energy_field/Bumped(atom/A)
..(A)
impact_effect(2)
//if we take too much damage, drop out - the generator will bring us back up if we have enough power
/obj/effect/energy_field/handle_meteor_impact(var/obj/effect/meteor/meteor)
var/penetrated = TRUE
adjust_strength(-max((meteor.wall_power * meteor.hits) / 800, 0)) // One renwick (strength var) equals one r-wall for the purposes of meteor-stopping.
sleep(1)
if(density) // Check if we're still up.
penetrated = FALSE
explosion(meteor.loc, 0, 0, 0, 0, 0, 0, 0) // For the sound effect.
// Returning FALSE will kill the meteor.
return penetrated // If the shield's still around, the meteor was successfully stopped, otherwise keep going and plow into the station.
/obj/effect/energy_field/proc/adjust_strength(amount, impact = 1)
var/old_density = density
if(strength >= 1)
invisibility = 0
density = 1
else if(strength < 1)
invisibility = 101
density = 0
if (density != old_density)
strength = between(0, strength + amount, max_strength)
//maptext = "[round(strength, 0.1)]/[max_strength]"
//if we take too much damage, drop out - the generator will bring us back up if we have enough power
if(amount < 0) // Taking damage.
if(impact)
impact_effect(round(abs(amount * 2)))
ticks_recovering = min(ticks_recovering + 2, 10)
if(strength < 1) // We broke
density = 0
ticks_recovering = 10
strength = 0
else if(amount > 0) // Healing damage.
if(strength >= 1)
density = 1
if(density != old_density)
update_icon()
update_nearby_tiles()
/obj/effect/energy_field/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
@@ -66,3 +105,44 @@
//return (!density || !height || air_group)
return !density
/obj/effect/energy_field/update_icon(var/update_neightbors = 0)
overlays.Cut()
var/list/adjacent_shields_dir = list()
for(var/direction in cardinal)
var/turf/T = get_step(src, direction)
if(T) // Incase we somehow stepped off the map.
for(var/obj/effect/energy_field/F in T)
if(update_neightbors)
F.update_icon(0)
adjacent_shields_dir |= direction
break
// Icon_state and Glow
if(density)
icon_state = "shield"
set_light(3, 3, "#66FFFF")
else
icon_state = "shield_broken"
set_light(3, 5, "#FF9900")
// Edge overlays
for(var/found_dir in adjacent_shields_dir)
overlays += image(src.icon, src, icon_state = "shield_edge", dir = found_dir)
// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment, and spreads to nearby shields.
/obj/effect/energy_field/proc/impact_effect(var/i, var/list/affected_shields = list())
i = between(1, i, 10)
alpha = 200
animate(src, alpha = initial(alpha), time = 1 SECOND)
affected_shields |= src
i--
if(i)
spawn(2)
for(var/direction in cardinal)
var/turf/T = get_step(src, direction)
if(T) // Incase we somehow stepped off the map.
for(var/obj/effect/energy_field/F in T)
if(!(F in affected_shields))
F.impact_effect(i, affected_shields) // Spread the effect to them.
@@ -0,0 +1,49 @@
/obj/item/weapon/shield_diffuser
name = "portable shield diffuser"
desc = "A small handheld device designed to disrupt energy barriers"
description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern), in a similar way the floor mounted variant does. It is, however, portable and run by an internal battery. Can be recharged with a regular recharger."
icon = 'icons/obj/machines/shielding.dmi'
icon_state = "hdiffuser_off"
var/obj/item/weapon/cell/device/cell
var/enabled = 0
/obj/item/weapon/shield_diffuser/update_icon()
if(enabled)
icon_state = "hdiffuser_on"
else
icon_state = "hdiffuser_off"
/obj/item/weapon/shield_diffuser/New()
cell = new(src)
..()
/obj/item/weapon/shield_diffuser/Destroy()
qdel(cell)
cell = null
if(enabled)
processing_objects.Remove(src)
. = ..()
/obj/item/weapon/shield_diffuser/process()
if(!enabled)
return
for(var/direction in cardinal)
var/turf/simulated/shielded_tile = get_step(get_turf(src), direction)
for(var/obj/effect/energy_field/S in shielded_tile)
if(istype(S) && cell.checked_use(10 KILOWATTS * CELLRATE))
qdel(S)
/obj/item/weapon/shield_diffuser/attack_self()
enabled = !enabled
update_icon()
if(enabled)
processing_objects.Add(src)
else
processing_objects.Remove(src)
to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].")
/obj/item/weapon/shield_diffuser/examine()
. = ..()
to_chat(usr, "The charge meter reads [cell ? cell.percent() : 0]%")
to_chat(usr, "It is [enabled ? "enabled" : "disabled"].")
+7 -15
View File
@@ -19,14 +19,6 @@
var/charge_rate = 100000 //100 kW
var/obj/machinery/shield_gen/owned_gen
/obj/machinery/shield_capacitor/New()
spawn(10)
for(var/obj/machinery/shield_gen/possible_gen in range(1, src))
if(get_dir(src, possible_gen) == src.dir)
possible_gen.owned_capacitor = src
break
..()
/obj/machinery/shield_capacitor/emag_act(var/remaining_charges, var/mob/user)
if(prob(75))
src.locked = !src.locked
@@ -54,13 +46,13 @@
if(anchored)
spawn(0)
for(var/obj/machinery/shield_gen/gen in range(1, src))
if(get_dir(src, gen) == src.dir && !gen.owned_capacitor)
if(get_dir(src, gen) == src.dir)
owned_gen = gen
owned_gen.owned_capacitor = src
owned_gen.capacitors |= src
owned_gen.updateDialog()
else
if(owned_gen && owned_gen.owned_capacitor == src)
owned_gen.owned_capacitor = null
if(owned_gen && src in owned_gen.capacitors)
owned_gen.capacitors -= src
owned_gen = null
else
..()
@@ -82,16 +74,16 @@
else
t += "This capacitor is: [active ? "<font color=green>Online</font>" : "<font color=red>Offline</font>" ] <a href='?src=\ref[src];toggle=1'>[active ? "\[Deactivate\]" : "\[Activate\]"]</a><br>"
t += "Capacitor Status: [time_since_fail > 2 ? "<font color=green>OK.</font>" : "<font color=red>Discharging!</font>"]<br>"
t += "Stored Energy: [round(stored_charge/1000, 0.1)] kJ ([100 * round(stored_charge/max_charge, 0.1)]%)<br>"
t += "Stored Energy: [format_SI(stored_charge, "J")] ([100 * round(stored_charge/max_charge, 0.01)]%)<br>"
t += "Charge Rate: \
<a href='?src=\ref[src];charge_rate=-100000'>\[----\]</a> \
<a href='?src=\ref[src];charge_rate=-10000'>\[---\]</a> \
<a href='?src=\ref[src];charge_rate=-1000'>\[--\]</a> \
<a href='?src=\ref[src];charge_rate=-100'>\[-\]</a>[charge_rate] W \
<a href='?src=\ref[src];charge_rate=-100'>\[-\]</a>[format_SI(charge_rate, "W")]\
<a href='?src=\ref[src];charge_rate=100'>\[+\]</a> \
<a href='?src=\ref[src];charge_rate=1000'>\[++\]</a> \
<a href='?src=\ref[src];charge_rate=10000'>\[+++\]</a> \
<a href='?src=\ref[src];charge_rate=100000'>\[+++\]</a><br>"
<a href='?src=\ref[src];charge_rate=100000'>\[++++\]</a><br>"
t += "<hr>"
t += "<A href='?src=\ref[src]'>Refresh</A> "
t += "<A href='?src=\ref[src];close=1'>Close</A><BR>"
+74
View File
@@ -0,0 +1,74 @@
/obj/machinery/shield_diffuser
name = "shield diffuser"
desc = "A small underfloor device specifically designed to disrupt energy barriers."
description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access."
icon = 'icons/obj/machines/shielding.dmi'
icon_state = "fdiffuser_on"
use_power = 2
idle_power_usage = 100
active_power_usage = 2000
anchored = 1
density = 0
level = 1
var/alarm = 0
var/enabled = 1
/obj/machinery/shield_diffuser/New()
..()
var/turf/T = get_turf(src)
hide(!T.is_plating())
//If underfloor, hide the cable
/obj/machinery/shield_diffuser/hide(var/i)
if(istype(loc, /turf))
invisibility = i ? 101 : 0
update_icon()
/obj/machinery/shield_diffuser/hides_under_flooring()
return 1
/obj/machinery/shield_diffuser/process()
if(alarm)
alarm--
if(!alarm)
update_icon()
return
if(!enabled)
return
for(var/direction in cardinal)
var/turf/simulated/shielded_tile = get_step(get_turf(src), direction)
for(var/obj/effect/energy_field/S in shielded_tile)
qdel(S)
/obj/machinery/shield_diffuser/update_icon()
if(alarm)
icon_state = "fdiffuser_emergency"
return
if((stat & (NOPOWER | BROKEN)) || !enabled)
icon_state = "fdiffuser_off"
else
icon_state = "fdiffuser_on"
/obj/machinery/shield_diffuser/attack_hand()
if(alarm)
to_chat(usr, "You press an override button on \the [src], re-enabling it.")
alarm = 0
update_icon()
return
enabled = !enabled
use_power = enabled + 1
update_icon()
to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].")
/obj/machinery/shield_diffuser/proc/meteor_alarm(var/duration)
if(!duration)
return
alarm = round(max(alarm, duration))
update_icon()
/obj/machinery/shield_diffuser/examine(var/mob/user)
. = ..()
to_chat(user, "It is [enabled ? "enabled" : "disabled"].")
if(alarm)
to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.")
+91 -35
View File
@@ -1,10 +1,3 @@
//renwicks: fictional unit to describe shield strength
//a small meteor hit will deduct 1 renwick of strength from that shield tile
//light explosion range will do 1 renwick's damage
//medium explosion range will do 2 renwick's damage
//heavy explosion range will do 3 renwick's damage
//explosion damage is cumulative. if a tile is in range of light, medium and heavy damage, it will take a hit from all three
/obj/machinery/shield_gen
name = "bubble shield generator"
desc = "Machine that generates an impenetrable field of energy when activated."
@@ -13,7 +6,7 @@
var/active = 0
var/field_radius = 3
var/max_field_radius = 100
var/list/field
var/list/field = list()
density = 1
var/locked = 0
var/average_field_strength = 0
@@ -23,20 +16,25 @@
var/min_dissipation = 0.01 //will dissipate by at least this rate in renwicks per field tile (otherwise field would never dissipate completely as dissipation is a percentage)
var/powered = 0
var/check_powered = 1
var/obj/machinery/shield_capacitor/owned_capacitor
var/list/capacitors = list()
var/target_field_strength = 10
var/max_field_strength = 10
var/time_since_fail = 100
var/energy_conversion_rate = 0.0002 //how many renwicks per watt?
var/z_range = 0 // How far 'up and or down' to extend the shield to, in z-levels. Only works on MultiZ supported z-levels.
use_power = 0 //doesn't use APC power
/obj/machinery/shield_gen/New()
spawn(10)
for(var/obj/machinery/shield_capacitor/possible_cap in range(1, src))
if(get_dir(possible_cap, src) == possible_cap.dir)
owned_capacitor = possible_cap
break
field = new/list()
spawn(1 SECOND)
if(anchored)
for(var/obj/machinery/shield_capacitor/cap in range(1, src))
if(!cap.anchored)
continue
if(cap.owned_gen)
continue
if(get_dir(cap, src) == cap.dir)
capacitors |= cap
cap.owned_gen = src
..()
/obj/machinery/shield_gen/Destroy()
@@ -44,7 +42,7 @@
field.Remove(D)
D.loc = null
..()
/obj/machinery/shield_gen/emag_act(var/remaining_charges, var/mob/user)
if(prob(75))
src.locked = !src.locked
@@ -76,14 +74,14 @@
if(cap.owned_gen)
continue
if(get_dir(cap, src) == cap.dir && src.anchored)
owned_capacitor = cap
owned_capacitor.owned_gen = src
// owned_capacitor = cap
capacitors |= cap
cap.owned_gen = src
updateDialog()
break
// break
else
if(owned_capacitor && owned_capacitor.owned_gen == src)
owned_capacitor.owned_gen = null
owned_capacitor = null
for(var/obj/machinery/shield_capacitor/capacitor in capacitors)
capacitor.owned_gen = null
else
..()
@@ -105,7 +103,13 @@
if(locked)
t += "<i>Swipe your ID card to begin.</i>"
else
t += "[owned_capacitor ? "<font color=green>Charge capacitor connected.</font>" : "<font color=red>Unable to locate charge capacitor!</font>"]<br>"
t += "[capacitors.len ? "<font color=green>Charge capacitor(s) connected.</font>" : "<font color=red>Unable to locate charge capacitor!</font>"]<br>"
var/i = 0
for(var/obj/machinery/shield_capacitor/capacitor in capacitors)
i++
t += "Capacitor #[i]: [capacitor.active ? "<font color=green>Online.</font>" : "<font color=red>Offline.</font>"] \
Charge: [round(capacitor.stored_charge/1000, 0.1)] kJ ([100 * round(capacitor.stored_charge/capacitor.max_charge, 0.01)]%) \
Status: [capacitor.time_since_fail > 2 ? "<font color=green>OK.</font>" : "<font color=red>Discharging!</font>"]<br>"
t += "This generator is: [active ? "<font color=green>Online</font>" : "<font color=red>Offline</font>" ] <a href='?src=\ref[src];toggle=1'>[active ? "\[Deactivate\]" : "\[Activate\]"]</a><br>"
t += "Field Status: [time_since_fail > 2 ? "<font color=green>Stable</font>" : "<font color=red>Unstable</font>"]<br>"
t += "Coverage Radius (restart required): \
@@ -116,12 +120,17 @@
<a href='?src=\ref[src];change_radius=1'>+</a> \
<a href='?src=\ref[src];change_radius=5'>++</a> \
<a href='?src=\ref[src];change_radius=50'>+++</a><br>"
if(HasAbove(src.z) || HasBelow(src.z)) // Won't show up on maps lacking MultiZ support.
t += "Vertical Shielding (restart required): \
<a href='?src=\ref[src];z_range=-1'>-</a> \
[z_range] Vertical Range \
<a href='?src=\ref[src];z_range=1'>+</a><br>"
t += "Overall Field Strength: [round(average_field_strength, 0.01)] Renwick ([target_field_strength ? round(100 * average_field_strength / target_field_strength, 0.1) : "NA"]%)<br>"
t += "Upkeep Power: [round(field.len * max(average_field_strength * dissipation_rate, min_dissipation) / energy_conversion_rate)] W<br>"
t += "Upkeep Power: [format_SI(round(field.len * max(average_field_strength * dissipation_rate, min_dissipation) / energy_conversion_rate), "W")]<br>"
t += "Charge Rate: <a href='?src=\ref[src];strengthen_rate=-0.1'>--</a> \
[strengthen_rate] Renwick/s \
<a href='?src=\ref[src];strengthen_rate=0.1'>++</a><br>"
t += "Shield Generation Power: [round(field.len * min(strengthen_rate, target_field_strength - average_field_strength) / energy_conversion_rate)] W<br>"
t += "Shield Generation Power: [format_SI(round(field.len * min(strengthen_rate, target_field_strength - average_field_strength) / energy_conversion_rate), "W")]<br>"
t += "Maximum Field Strength: \
<a href='?src=\ref[src];target_field_strength=-10'>\[min\]</a> \
<a href='?src=\ref[src];target_field_strength=-5'>--</a> \
@@ -148,13 +157,28 @@
var/renwick_upkeep_per_field = max(average_field_strength * dissipation_rate, min_dissipation)
//figure out how much energy we need to draw from the capacitor
if(active && owned_capacitor && owned_capacitor.active)
if(active && capacitors.len)
// Get a list of active capacitors to drain from.
var/list/active_capacitors = list()
for(var/obj/machinery/shield_capacitor/capacitor in capacitors) // Some capacitors might be off. Exclude them.
if(capacitor.active && capacitor.stored_charge > 0)
active_capacitors |= capacitor
var/target_renwick_increase = min(target_field_strength - average_field_strength, strengthen_rate) + renwick_upkeep_per_field //per field tile
var/required_energy = field.len * target_renwick_increase / energy_conversion_rate
var/assumed_charge = min(owned_capacitor.stored_charge, required_energy)
// Gets the charge for all capacitors
var/sum_charge = 0
for(var/obj/machinery/shield_capacitor/capacitor in active_capacitors)
sum_charge += capacitor.stored_charge
var/assumed_charge = min(sum_charge, required_energy)
total_renwick_increase = assumed_charge * energy_conversion_rate
owned_capacitor.stored_charge -= assumed_charge
for(var/obj/machinery/shield_capacitor/capacitor in active_capacitors)
capacitor.stored_charge -= max(assumed_charge / active_capacitors.len, 0) // Drain from all active capacitors evenly.
else
renwick_upkeep_per_field = max(renwick_upkeep_per_field, 0.5)
@@ -162,12 +186,13 @@
average_field_strength = 0 //recalculate the average field strength
for(var/obj/effect/energy_field/E in field)
E.max_strength = target_field_strength
var/amount_to_strengthen = renwick_increase_per_field - renwick_upkeep_per_field
if(E.ticks_recovering > 0 && amount_to_strengthen > 0)
E.Strengthen( min(amount_to_strengthen / 10, 0.1) )
E.adjust_strength( min(amount_to_strengthen / 10, 0.1), 0 )
E.ticks_recovering -= 1
else
E.Strengthen(amount_to_strengthen)
E.adjust_strength(amount_to_strengthen, 0)
average_field_strength += E.strength
@@ -194,6 +219,8 @@
strengthen_rate = between(0, strengthen_rate + text2num(href_list["strengthen_rate"]), max_strengthen_rate)
else if( href_list["target_field_strength"] )
target_field_strength = between(1, target_field_strength + text2num(href_list["target_field_strength"]), max_field_strength)
else if( href_list["z_range"] )
z_range = between(0, z_range + text2num(href_list["z_range"]), 10) // Max is extending ten z-levels up and down. Probably too big of a number but it shouldn't matter.
updateDialog()
@@ -213,16 +240,19 @@
if(T in covered_turfs)
covered_turfs.Remove(T)
for(var/turf/O in covered_turfs)
var/obj/effect/energy_field/E = new(O)
var/obj/effect/energy_field/E = new(O, src)
field.Add(E)
covered_turfs = null
for(var/mob/M in view(5,src))
M << "\icon[src] You hear heavy droning start up."
for(var/obj/effect/energy_field/E in field) // Update the icons here to ensure all the shields have been made already.
E.update_icon()
else
for(var/obj/effect/energy_field/D in field)
field.Remove(D)
D.loc = null
//D.loc = null
qdel(D)
for(var/mob/M in view(5,src))
M << "\icon[src] You hear heavy droning fade out."
@@ -236,12 +266,38 @@
else
icon_state = "generator0"
//TODO MAKE THIS MULTIZ COMPATIBLE
//grab the border tiles in a circle around this machine
/obj/machinery/shield_gen/proc/get_shielded_turfs()
var/list/out = list()
var/turf/gen_turf = get_turf(src)
var/turf/T = get_turf(src)
if (!T)
return
out += get_shielded_turfs_on_z_level(T)
if(z_range)
var/i = z_range
while(HasAbove(T.z) && i)
T = GetAbove(T)
i--
if(istype(T))
out += get_shielded_turfs_on_z_level(T)
T = get_turf(src)
i = z_range
while(HasBelow(T.z) && i)
T = GetBelow(T)
i--
if(istype(T))
out += get_shielded_turfs_on_z_level(T)
return out
/obj/machinery/shield_gen/proc/get_shielded_turfs_on_z_level(var/turf/gen_turf)
var/list/out = list()
if (!gen_turf)
return
@@ -260,4 +316,4 @@
T = locate(gen_turf.x + field_radius, gen_turf.y + y_offset, gen_turf.z)
if (T) out += T
return out
return out
@@ -7,15 +7,13 @@
/obj/machinery/shield_gen/external/New()
..()
//NOT MULTIZ COMPATIBLE
//Search for space turfs within range that are adjacent to a simulated turf.
/obj/machinery/shield_gen/external/get_shielded_turfs()
/obj/machinery/shield_gen/external/get_shielded_turfs_on_z_level(var/turf/gen_turf)
var/list/out = list()
var/turf/gen_turf = get_turf(src)
if (!gen_turf)
return
var/turf/T
for (var/x_offset = -field_radius; x_offset <= field_radius; x_offset++)
for (var/y_offset = -field_radius; y_offset <= field_radius; y_offset++)
@@ -24,4 +22,4 @@
//check neighbors of T
if (locate(/turf/simulated/) in orange(1, T))
out += T
return out
return out
+2 -1
View File
@@ -12,7 +12,8 @@
return 1
if(locate(/obj/structure/table/bench) in get_turf(mover))
return 0
if(locate(/obj/structure/table) in get_turf(mover))
var/obj/structure/table/table = locate(/obj/structure/table) in get_turf(mover)
if(table && !table.flipped)
return 1
return 0
@@ -30,9 +30,9 @@
..()
for(var/obj/effect/energy_field/E in created_field)
if(E.strength < 1)
E.Strengthen(0.15)
E.adjust_strength(0.15, 0)
else if(E.strength < 5)
E.Strengthen(0.25)
E.adjust_strength(0.25, 0)
/datum/artifact_effect/forcefield/UpdateMove()
if(created_field.len && holder)