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
@@ -19,7 +19,7 @@
var/hibernate = 0 //Do we even process?
var/scrubbing = 1 //0 = siphoning, 1 = scrubbing
var/list/scrubbing_gas = list("carbon_dioxide")
var/list/scrubbing_gas = list("carbon_dioxide", "phoron")
var/panic = 0 //is this scrubber panicked?
+45
View File
@@ -4,3 +4,48 @@
for(type in view(range, dview_mob))
#define END_FOR_DVIEW dview_mob.loc = null
#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square
#define LIGHTING_LAMBERTIAN 0 // 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_LAYER 10 // drawing layer for lighting overlays
#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects
#define LIGHTING_ICON_STATE_DARK "soft_dark" // Change between "soft_dark" and "dark" to swap soft darkvision
#define LIGHTING_ROUND_VALUE (1 / 64) // Value used to round lumcounts, values smaller than 1/69 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_SOFT_THRESHOLD 0.05 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. This also should be the transparancy of the "soft_dark" icon state.
// If I were you I'd leave this alone.
#define LIGHTING_BASE_MATRIX \
list \
( \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
0, 0, 0, 1 \
) \
// Helpers so we can (more easily) control the colour matrices.
#define CL_MATRIX_RR 1
#define CL_MATRIX_RG 2
#define CL_MATRIX_RB 3
#define CL_MATRIX_RA 4
#define CL_MATRIX_GR 5
#define CL_MATRIX_GG 6
#define CL_MATRIX_GB 7
#define CL_MATRIX_GA 8
#define CL_MATRIX_BR 9
#define CL_MATRIX_BG 10
#define CL_MATRIX_BB 11
#define CL_MATRIX_BA 12
#define CL_MATRIX_AR 13
#define CL_MATRIX_AG 14
#define CL_MATRIX_AB 15
#define CL_MATRIX_AA 16
#define CL_MATRIX_CR 17
#define CL_MATRIX_CG 18
#define CL_MATRIX_CB 19
#define CL_MATRIX_CA 20
+4
View File
@@ -3,6 +3,10 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called
#define CELLRATE 0.002 // Multiplier for watts per tick <> cell storage (e.g., 0.02 means if there is a load of 1000 watts, 20 units will be taken from a cell per second)
// It's a conversion constant. power_used*CELLRATE = charge_provided, or charge_used/CELLRATE = power_provided
#define KILOWATTS *1000
#define MEGAWATTS *1000000
#define GIGAWATTS *1000000000
// Doors!
#define DOOR_CRUSH_DAMAGE 20
#define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien.
+14 -1
View File
@@ -952,7 +952,7 @@ proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0)
// var/area/AR = X.loc
// if(AR.lighting_use_dynamic)
// if(AR.dynamic_lighting)
// X.opacity = !X.opacity
// X.sd_SetOpacity(!X.opacity) //TODO: rewrite this code so it's not messed by lighting ~Carn
@@ -1299,3 +1299,16 @@ var/mob/dview/dview_mob = new
tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1))))
return locate(tX, tY, tZ)
// Displays something as commonly used (non-submultiples) SI units.
/proc/format_SI(var/number, var/symbol)
switch(round(abs(number)))
if(0 to 1000-1)
return "[number] [symbol]"
if(1e3 to 1e6-1)
return "[round(number / 1000, 0.1)] k[symbol]" // kilo
if(1e6 to 1e9-1)
return "[round(number / 1e6, 0.1)] M[symbol]" // mega
if(1e9 to 1e12-1) // Probably not needed but why not be complete?
return "[round(number / 1e9, 0.1)] G[symbol]" // giga
if(1e12 to 1e15-1)
return "[round(number / 1e12, 0.1)] T[symbol]" // tera
+98
View File
@@ -0,0 +1,98 @@
/var/lighting_overlays_initialised = FALSE
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated.
/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated.
/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated.
/datum/controller/process/lighting
// Queues of update counts, waiting to be rolled into stats lists
var/list/stats_queues = list(
"Source" = list(), "Corner" = list(), "Overlay" = list())
// Stats lists
var/list/stats_lists = list(
"Source" = list(), "Corner" = list(), "Overlay" = list())
var/update_stats_every = (1 SECONDS)
var/next_stats_update = 0
var/stat_updates_to_keep = 5
/datum/controller/process/lighting/setup()
name = "lighting"
schedule_interval = 0 // run as fast as you possibly can
sleep_interval = 10 // Yield every 10% of a tick
defer_usage = 80 // Defer at 80% of a tick
create_all_lighting_overlays()
lighting_overlays_initialised = TRUE
// Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load.
spawn(300)
doWork(1)
/datum/controller/process/lighting/doWork(roundstart)
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 = list()
for(var/datum/light_source/L in lighting_update_lights_old)
if(L.check() || L.destroyed || 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 = FALSE
L.force_update = FALSE
L.needs_update = FALSE
SCHECK
lighting_update_corners_old = lighting_update_corners //Same as above.
lighting_update_corners = list()
for(var/A in lighting_update_corners_old)
var/datum/lighting_corner/C = A
C.update_overlays()
C.needs_update = FALSE
SCHECK
lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = list()
for(var/A in lighting_update_overlays_old)
var/atom/movable/lighting_overlay/O = A
O.update_overlay()
O.needs_update = 0
SCHECK
stats_queues["Source"] += lighting_update_lights_old.len
stats_queues["Corner"] += lighting_update_corners_old.len
stats_queues["Overlay"] += lighting_update_overlays_old.len
if(next_stats_update <= world.time)
next_stats_update = world.time + update_stats_every
for(var/stat_name in stats_queues)
var/stat_sum = 0
var/list/stats_queue = stats_queues[stat_name]
for(var/count in stats_queue)
stat_sum += count
stats_queue.Cut()
var/list/stats_list = stats_lists[stat_name]
stats_list.Insert(1, stat_sum)
if(stats_list.len > stat_updates_to_keep)
stats_list.Cut(stats_list.len)
/datum/controller/process/lighting/statProcess()
..()
stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays")
for(var/stat_type in stats_lists)
stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]")
+16 -1
View File
@@ -25,6 +25,21 @@
catchException(e, last_object)
SCHECK
// We've been restarted, probably due to having a massive list of tasks.
// Lets copy over the task list as safely as we can and try to chug thru it...
// Note: We won't be informed about tasks being destroyed, but this is the best we can do.
/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target)
scheduled_tasks = list()
for(var/st in target.scheduled_tasks)
if(!deleted(st) && istype(st, /datum/scheduled_task))
schedule(st)
scheduler = src
// We are being killed. Least we can do is deregister all those events we registered
/datum/controller/process/scheduler/onKill()
for(var/st in scheduled_tasks)
destroyed_event.unregister(st, src)
/datum/controller/process/scheduler/statProcess()
..()
stat(null, "[scheduled_tasks.len] task\s")
@@ -130,4 +145,4 @@
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
st.trigger_time = world.time + trigger_delay
scheduler.schedule(st)
scheduler.schedule(st)
+16 -16
View File
@@ -100,7 +100,7 @@ var/list/ghostteleportlocs = list()
icon_state = "space"
requires_power = 1
always_unpowered = 1
lighting_use_dynamic = 1
dynamic_lighting = 1
power_light = 0
power_equip = 0
power_environ = 0
@@ -293,7 +293,7 @@ area/space/atmosalert()
/area/shuttle/mining
name = "\improper Mining Elevator"
music = "music/escape.ogg"
lighting_use_dynamic = 0
dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/mining/station
@@ -392,7 +392,7 @@ area/space/atmosalert()
/area/shuttle/research
name = "\improper Research Elevator"
music = "music/escape.ogg"
lighting_use_dynamic = 0
dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/research/station
@@ -418,7 +418,7 @@ area/space/atmosalert()
name = "\improper CentCom"
icon_state = "centcom"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/centcom/control
name = "\improper CentCom Control"
@@ -491,7 +491,7 @@ area/space/atmosalert()
name = "\improper Mercenary Base"
icon_state = "syndie-ship"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/syndicate_mothership/control
name = "\improper Mercenary Control Room"
@@ -543,7 +543,7 @@ area/space/atmosalert()
name = "\improper Thunderdome"
icon_state = "thunder"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
sound_env = ARENA
/area/tdome/tdome1
@@ -624,7 +624,7 @@ area/space/atmosalert()
name = "\improper Wizard's Den"
icon_state = "yellow"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/skipjack_station
name = "\improper Skipjack"
@@ -1485,7 +1485,7 @@ area/space/atmosalert()
/area/holodeck
name = "\improper Holodeck"
icon_state = "Holodeck"
lighting_use_dynamic = 0
dynamic_lighting = 0
sound_env = LARGE_ENCLOSED
/area/holodeck/alphadeck
@@ -1638,7 +1638,7 @@ area/space/atmosalert()
/area/solar
requires_power = 1
always_unpowered = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
base_turf = /turf/space
auxport
@@ -2336,7 +2336,7 @@ area/space/atmosalert()
/area/shuttle/constructionsite
name = "\improper Construction Site Shuttle"
icon_state = "yellow"
lighting_use_dynamic = 0
dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/constructionsite/station
@@ -2493,25 +2493,25 @@ area/space/atmosalert()
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/AIsatextFS
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/AIsatextAS
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/AIsatextAP
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/NewAIMain
name = "\improper AI Main New"
@@ -2690,7 +2690,7 @@ area/space/atmosalert()
name = "Beach"
icon_state = "null"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
requires_power = 0
ambience = list()
// Vorestation Removal - This is very broken. Sounds do not stop when you leave the area.
@@ -2813,7 +2813,7 @@ var/list/the_station_areas = list (
name = "Keelin's private beach"
icon_state = "null"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
requires_power = 0
var/sound/mysound = null
/*
+1 -1
View File
@@ -18,7 +18,7 @@
power_equip = 0
power_environ = 0
if(lighting_use_dynamic)
if(dynamic_lighting)
luminosity = 0
else
luminosity = 1
+1 -1
View File
@@ -126,7 +126,7 @@
/area/outpost/engineering/solarsoutside
requires_power = 1
always_unpowered = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
aft
name = "\improper Engineering Outpost Solar Array"
+6
View File
@@ -40,7 +40,13 @@
reagents = null
for(var/atom/movable/AM in contents)
qdel(AM)
var/turf/un_opaque
if(opacity && isturf(loc))
un_opaque = loc
loc = null
if(un_opaque)
un_opaque.recalc_atom_opacity()
if (pulledby)
if (pulledby.pulling == src)
pulledby.pulling = null
@@ -28,6 +28,7 @@
C.reagents.clear_reagents()
C.ingested.clear_reagents()
var/heal_amount = 5
if(src.mind.changeling.recursive_enhancement)
heal_amount = heal_amount * 2
@@ -38,5 +39,17 @@
C.adjustToxLoss(-heal_amount)
sleep(10)
for(var/obj/item/organ/external/E in C.organs)
var/obj/item/organ/external/G = E
if(G.germ_level)
var/germ_heal = heal_amount * 100
G.germ_level = min(0, G.germ_level - germ_heal)
for(var/obj/item/organ/internal/I in C.internal_organs)
var/obj/item/organ/internal/G = I
if(G.germ_level)
var/germ_heal = heal_amount * 100
G.germ_level = min(0, G.germ_level - germ_heal)
feedback_add_details("changeling_powers","AP")
return 1
+1 -1
View File
@@ -62,7 +62,7 @@ In short:
/datum/universal_state/hell/OverlayAndAmbientSet()
spawn(0)
for(var/atom/movable/lighting_overlay/L in world)
for(var/datum/lighting_corner/L in world)
L.update_lumcount(1, 0, 0)
for(var/turf/space/T in turfs)
@@ -93,7 +93,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked
/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet()
spawn(0)
for(var/atom/movable/lighting_overlay/L in world)
for(var/datum/lighting_corner/L in world)
if(L.z in using_map.admin_levels)
L.update_lumcount(1,1,1)
else
+66 -31
View File
@@ -80,6 +80,10 @@
var/turf/T = locate(endx, endy, Z)
return T
// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'.
/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor)
return TRUE
///////////////////////
//The meteor effect
//////////////////////
@@ -101,6 +105,11 @@
var/meteordrop = /obj/item/weapon/ore/iron
var/dropamt = 2
// How much damage it does to walls, using take_damage().
// Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through.
// Multiply this and the hits var to get a rough idea of how penetrating a meteor is.
var/wall_power = 100
/obj/effect/meteor/New()
..()
z_original = z
@@ -132,8 +141,11 @@
/obj/effect/meteor/Bump(atom/A)
if(A)
ram_turf(get_turf(A))
get_hit()
if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield.
ram_turf(get_turf(A))
get_hit()
else
die(0)
/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
return istype(mover, /obj/effect/meteor) ? 1 : ..()
@@ -146,7 +158,11 @@
//then, ram the turf if it still exists
if(T)
T.ex_act(hitpwr)
if(istype(T, /turf/simulated/wall))
var/turf/simulated/wall/W = T
W.take_damage(wall_power) // Stronger walls can halt asteroids.
else
T.ex_act(hitpwr) // Floors and other things lack fancy health.
//process getting 'hit' by colliding with a dense object
@@ -154,9 +170,12 @@
/obj/effect/meteor/proc/get_hit()
hits--
if(hits <= 0)
make_debris()
meteor_effect()
qdel(src)
die(1)
/obj/effect/meteor/proc/die(var/explode = 1)
make_debris()
meteor_effect(explode)
qdel(src)
/obj/effect/meteor/ex_act()
return
@@ -172,21 +191,24 @@
var/obj/item/O = new meteordrop(get_turf(src))
O.throw_at(dest, 5, 10)
/obj/effect/meteor/proc/meteor_effect()
/obj/effect/meteor/proc/shake_players()
for(var/mob/M in player_list)
var/turf/T = get_turf(M)
if(!T || T.z != src.z)
continue
var/dist = get_dist(M.loc, src.loc)
shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3)
/obj/effect/meteor/proc/meteor_effect(var/explode)
if(heavy)
for(var/mob/M in player_list)
var/turf/T = get_turf(M)
if(!T || T.z != src.z)
continue
var/dist = get_dist(M.loc, src.loc)
shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3)
shake_players()
///////////////////////
//Meteor types
///////////////////////
//Dust
// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot.
/obj/effect/meteor/dust
name = "space dust"
icon_state = "dust"
@@ -194,63 +216,74 @@
hits = 1
hitpwr = 3
meteordrop = /obj/item/weapon/ore/glass
wall_power = 50
//Medium-sized
// Medium-sized meteors aren't very special and can be stopped easily by r-walls.
/obj/effect/meteor/medium
name = "meteor"
dropamt = 3
wall_power = 200
/obj/effect/meteor/medium/meteor_effect()
/obj/effect/meteor/medium/meteor_effect(var/explode)
..()
explosion(src.loc, 0, 1, 2, 3, 0)
if(explode)
explosion(src.loc, 0, 1, 2, 3, 0)
//Large-sized
// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter.
/obj/effect/meteor/big
name = "large meteor"
icon_state = "large"
hits = 6
hits = 8
heavy = 1
dropamt = 4
wall_power = 400
/obj/effect/meteor/big/meteor_effect()
/obj/effect/meteor/big/meteor_effect(var/explode)
..()
explosion(src.loc, 1, 2, 3, 4, 0)
if(explode)
explosion(src.loc, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 6, flash_range = 12, adminlog = 0)
//Flaming meteor
// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end.
/obj/effect/meteor/flaming
name = "flaming meteor"
icon_state = "flaming"
hits = 5
heavy = 1
meteordrop = /obj/item/weapon/ore/phoron
wall_power = 100
/obj/effect/meteor/flaming/meteor_effect()
/obj/effect/meteor/flaming/meteor_effect(var/explode)
..()
explosion(src.loc, 1, 2, 3, 4, 0, 0, 5)
if(explode)
explosion(src.loc, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 8, flash_range = 16, adminlog = 0)
//Radiation meteor
// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding.
/obj/effect/meteor/irradiated
name = "glowing meteor"
icon_state = "glowing"
heavy = 1
meteordrop = /obj/item/weapon/ore/uranium
wall_power = 75
/obj/effect/meteor/irradiated/meteor_effect()
/obj/effect/meteor/irradiated/meteor_effect(var/explode)
..()
explosion(src.loc, 0, 0, 4, 3, 0)
if(explode)
explosion(src.loc, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 4, flash_range = 6, adminlog = 0)
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
for(var/mob/living/L in view(5, src))
for(var/mob/living/L in view(10, src))
L.apply_effect(40, IRRADIATE)
// This meteor fries toasters.
/obj/effect/meteor/emp
name = "conducting meteor"
icon_state = "glowing_blue"
desc = "Hide your floppies!"
meteordrop = /obj/item/weapon/ore/osmium
dropamt = 3
wall_power = 80
/obj/effect/meteor/emp/meteor_effect()
/obj/effect/meteor/emp/meteor_effect(var/explode)
..()
// Best case scenario: Comparable to a low-yield EMP grenade.
// Worst case scenario: Comparable to a standard yield EMP grenade.
@@ -265,10 +298,12 @@
hitpwr = 1
heavy = 1
meteordrop = /obj/item/weapon/ore/phoron
wall_power = 150
/obj/effect/meteor/tunguska/meteor_effect()
/obj/effect/meteor/tunguska/meteor_effect(var/explode)
..()
explosion(src.loc, 5, 10, 15, 20, 0)
if(explode)
explosion(src.loc, 5, 10, 15, 20, 0)
/obj/effect/meteor/tunguska/Bump()
..()
@@ -32,5 +32,18 @@
H.adjustToxLoss(-heal_power / 5)
H.adjustCloneLoss(-heal_power / 5)
H.radiation = max(host.radiation - ( (heal_power * 2) / 5), 0)
for(var/obj/item/organ/external/E in H.organs)
var/obj/item/organ/external/G = E
if(G.germ_level)
var/germ_heal = heal_power * 10
G.germ_level = min(0, G.germ_level - germ_heal)
for(var/obj/item/organ/internal/I in H.internal_organs)
var/obj/item/organ/internal/G = I
if(G.germ_level)
var/germ_heal = heal_power * 10
G.germ_level = min(0, G.germ_level - germ_heal)
sleep(1 SECOND)
on_expire()
@@ -83,11 +83,8 @@
continue
if(!true_sight)
var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(L)
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(L)
var/light_amount = T.get_lumcount()
if(light_amount <= 0.5)
continue // Too dark to see.
+6 -1
View File
@@ -26,7 +26,12 @@
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/bartender(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/device/pda/bar(H), slot_belt)
return 1
if(has_alt_title(H, alt_title,"Bartender"))
if(H.backbag == 1)
H.equip_to_slot_or_del(new /obj/item/weapon/permit/gun/bar(H), slot_l_hand)
else
H.equip_to_slot_or_del(new /obj/item/weapon/permit/gun/bar(H.back), slot_in_backpack)
return 1
+3 -2
View File
@@ -93,7 +93,7 @@
req_access = list(access_rd, access_atmospherics, access_engine_equip)
TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa
TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa
TLV["phoron"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa
TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa
TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa
TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */
TLV["temperature"] = list(20, 40, 140, 160) // K
@@ -121,7 +121,7 @@
// breathable air according to human/Life()
TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa
TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa
TLV["phoron"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa
TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa
TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa
TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */
TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K
@@ -758,6 +758,7 @@
user << "<span class='notice'>You [ locked ? "lock" : "unlock"] the Air Alarm interface.</span>"
else
user << "<span class='warning'>Access denied.</span>"
return
return ..()
/obj/machinery/alarm/power_change()
+1 -1
View File
@@ -945,7 +945,7 @@ About the new airlock wires panel:
healthcheck()
/obj/effect/energy_field/airlock_crush(var/crush_damage)
Stress(crush_damage)
adjust_strength(crush_damage)
/obj/structure/closet/airlock_crush(var/crush_damage)
..()
+2 -2
View File
@@ -162,9 +162,9 @@
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
smoke.set_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
smoke.set_opacity(0) // lighting and view range updates
fadeOut(smoke)
qdel(src)
+56 -45
View File
@@ -66,7 +66,19 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/examine(mob/user)
if(..(user, 1))
user << "The time [stationtime2text()] is displayed in the corner of the screen."
to_chat(user, "The time [stationtime2text()] is displayed in the corner of the screen.")
/obj/item/device/pda/AltClick()
if(issilicon(usr))
return
if ( can_use(usr) )
if(id)
remove_id()
else
to_chat(usr, "<span class='notice'>This PDA does not have an ID in it.</span>")
/obj/item/device/pda/medical
default_cartridge = /obj/item/weapon/cartridge/medical
@@ -247,7 +259,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
set name = "Send Message"
set src in usr
if(usr.stat == 2)
usr << "You can't send PDA messages because you are dead!"
to_chat(usr, "You can't send PDA messages because you are dead!")
return
var/list/plist = available_pdas()
if (plist)
@@ -262,28 +274,27 @@ var/global/list/obj/item/device/pda/PDAs = list()
set name = "Toggle Sender/Receiver"
set src in usr
if(usr.stat == 2)
usr << "You can't do that because you are dead!"
to_chat(usr, "You can't send PDA messages because you are dead!")
return
toff = !toff
usr << "<span class='notice'>PDA sender/receiver toggled [(toff ? "Off" : "On")]!</span>"
to_chat(usr, "<span class='notice'>PDA sender/receiver toggled [(toff ? "Off" : "On")]!</span>")
/obj/item/device/pda/ai/verb/cmd_toggle_pda_silent()
set category = "AI IM"
set name = "Toggle Ringer"
set src in usr
if(usr.stat == 2)
usr << "You can't do that because you are dead!"
to_chat(usr, "You can't send PDA messages because you are dead!")
return
message_silent=!message_silent
usr << "<span class='notice'>PDA ringer toggled [(message_silent ? "Off" : "On")]!</span>"
to_chat(usr, "<span class='notice'>PDA ringer toggled [(message_silent ? "Off" : "On")]!</span>")
/obj/item/device/pda/ai/verb/cmd_show_message_log()
set category = "AI IM"
set name = "Show Message Log"
set src in usr
if(usr.stat == 2)
usr << "You can't do that because you are dead!"
to_chat(usr, "You can't send PDA messages because you are dead!")
return
var/HTML = "<html><head><title>AI PDA Message Log</title></head><body>"
for(var/index in tnote)
@@ -809,7 +820,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (in_range(src, U) && loc == U)
if (t)
if(src.hidden_uplink && hidden_uplink.check_trigger(U, lowertext(t), lowertext(lock_code)))
U << "The PDA softly beeps."
to_chat(U, "The PDA softly beeps.")
ui.close()
else
t = sanitize(t, 20)
@@ -856,7 +867,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
U.show_message("<span class='notice'>Virus sent!</span>", 1)
P.honkamt = (rand(15,20))
else
U << "PDA not found."
to_chat(U, "PDA not found.")
else
ui.close()
return 0
@@ -872,7 +883,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
P.ttone = "silence"
P.newstone = "silence"
else
U << "PDA not found."
to_chat(U, "PDA not found.")
else
ui.close()
return 0
@@ -928,9 +939,10 @@ var/global/list/obj/item/device/pda/PDAs = list()
message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded.", 1)
detonate_act(P)
else
U << "No charges left."
to_chat(U, "No charges left.")
else
U << "PDA not found."
to_chat(U, "PDA not found.")
else
U.unset_machine()
ui.close()
@@ -1048,7 +1060,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (ismob(loc))
var/mob/M = loc
M.put_in_hands(id)
usr << "<span class='notice'>You remove the ID from the [name].</span>"
to_chat(usr, "<span class='notice'>You remove the ID from the [name].</span>")
else
id.loc = get_turf(src)
id = null
@@ -1080,11 +1092,11 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(reception.message_server && (reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) // only send the message if it's stable
if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recipient have a broadcaster on their level?
U << "ERROR: Cannot reach recipient."
to_chat(U, "ERROR: Cannot reach recipient.")
return
var/send_result = reception.message_server.send_pda_message("[P.owner]","[owner]","[t]")
if (send_result)
U << "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'."
to_chat(U, "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'.")
return
tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]")))
@@ -1113,7 +1125,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
P.new_message_from_pda(src, t)
nanomanager.update_user_uis(U, src) // Update the sending user's PDA UI so that they can see the new message
else
U << "<span class='notice'>ERROR: Messaging server is not responding.</span>"
to_chat(U, "<span class='notice'>ERROR: Messaging server is not responding.</span>")
/obj/item/device/pda/proc/new_info(var/beep_silent, var/message_tone, var/reception_message)
if (!beep_silent)
@@ -1176,9 +1188,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(can_use(usr))
mode = 0
nanomanager.update_uis(src)
usr << "<span class='notice'>You press the reset button on \the [src].</span>"
to_chat(usr, "<span class='notice'>You press the reset button on \the [src].</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/obj/item/device/pda/verb/verb_remove_id()
set category = "Object"
@@ -1192,9 +1204,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(id)
remove_id()
else
usr << "<span class='notice'>This PDA does not have an ID in it.</span>"
to_chat(usr, "<span class='notice'>This PDA does not have an ID in it.</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/obj/item/device/pda/verb/verb_remove_pen()
@@ -1212,13 +1224,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/mob/M = loc
if(M.get_active_hand() == null)
M.put_in_hands(O)
usr << "<span class='notice'>You remove \the [O] from \the [src].</span>"
to_chat(usr, "<span class='notice'>You remove \the [O] from \the [src].</span>")
return
O.loc = get_turf(src)
else
usr << "<span class='notice'>This PDA does not have a pen in it.</span>"
to_chat(usr, "<span class='notice'>This PDA does not have a pen in it.</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/obj/item/device/pda/verb/verb_remove_cartridge()
set category = "Object"
@@ -1241,9 +1253,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (cartridge.radio)
cartridge.radio.hostpda = null
cartridge = null
usr << "<span class='notice'>You remove \the [cartridge] from the [name].</span>"
to_chat(usr, "<span class='notice'>You remove \the [cartridge] from the [name].</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use.
if(choice == 1)
@@ -1273,7 +1285,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
cartridge = C
user.drop_item()
cartridge.loc = src
user << "<span class='notice'>You insert [cartridge] into [src].</span>"
to_chat(usr, "<span class='notice'>You insert [cartridge] into [src].</span>")
nanomanager.update_uis(src) // update all UIs attached to src
if(cartridge.radio)
cartridge.radio.hostpda = src
@@ -1281,19 +1293,19 @@ var/global/list/obj/item/device/pda/PDAs = list()
else if(istype(C, /obj/item/weapon/card/id))
var/obj/item/weapon/card/id/idcard = C
if(!idcard.registered_name)
user << "<span class='notice'>\The [src] rejects the ID.</span>"
to_chat(user, "<span class='notice'>\The [src] rejects the ID.</span>")
return
if(!owner)
owner = idcard.registered_name
ownjob = idcard.assignment
ownrank = idcard.rank
name = "PDA-[owner] ([ownjob])"
user << "<span class='notice'>Card scanned.</span>"
to_chat(user, "<span class='notice'>Card scanned.</span>")
else
//Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand.
if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) )
if(id_check(user, 2))
user << "<span class='notice'>You put the ID into \the [src]'s slot.</span>"
to_chat(user, "<span class='notice'>You put the ID into \the [src]'s slot.</span>")
updateSelfDialog()//Update self dialog on success.
return //Return in case of failed check or when successful.
updateSelfDialog()//For the non-input related code.
@@ -1301,16 +1313,16 @@ var/global/list/obj/item/device/pda/PDAs = list()
user.drop_item()
C.loc = src
pai = C
user << "<span class='notice'>You slot \the [C] into [src].</span>"
to_chat(user, "<span class='notice'>You slot \the [C] into \the [src].</span>")
nanomanager.update_uis(src) // update all UIs attached to src
else if(istype(C, /obj/item/weapon/pen))
var/obj/item/weapon/pen/O = locate() in src
if(O)
user << "<span class='notice'>There is already a pen in \the [src].</span>"
to_chat(user, "<span class='notice'>There is already a pen in \the [src].</span>")
else
user.drop_item()
C.loc = src
user << "<span class='notice'>You slide \the [C] into \the [src].</span>"
to_chat(user, "<span class='notice'>You slot \the [C] into \the [src].</span>")
return
/obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob)
@@ -1346,18 +1358,18 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(2)
if (!istype(C:dna, /datum/dna))
user << "<span class='notice'>No fingerprints found on [C]</span>"
to_chat(user, "<span class='notice'>No fingerprints found on [C]</span>")
else
user << text("<span class='notice'>\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]</span>")
to_chat(user, text("<span class='notice'>\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]</span>"))
if ( !(C:blood_DNA) )
user << "<span class='notice'>No blood found on [C]</span>"
to_chat(user, "<span class='notice'>No blood found on [C]</span>")
if(C:blood_DNA)
qdel(C:blood_DNA)
else
user << "<span class='notice'>Blood found on [C]. Analysing...</span>"
to_chat(user, "<span class='notice'>Blood found on [C]. Analysing...</span>")
spawn(15)
for(var/blood in C:blood_DNA)
user << "<span class='notice'>Blood type: [C:blood_DNA[blood]]\nDNA: [blood]</span>"
to_chat(user, "<span class='notice'>Blood type: [C:blood_DNA[blood]]\nDNA: [blood]</span>")
if(4)
for (var/mob/O in viewers(C, null))
@@ -1379,13 +1391,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(!isnull(A.reagents))
if(A.reagents.reagent_list.len > 0)
var/reagents_length = A.reagents.reagent_list.len
user << "<span class='notice'>[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.</span>"
to_chat(user, "<span class='notice'>[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.</span>")
for (var/re in A.reagents.reagent_list)
user << "<span class='notice'> [re]</span>"
to_chat(user,"<span class='notice'> [re]</span>")
else
user << "<span class='notice'>No active chemical agents found in [A].</span>"
to_chat(user,"<span class='notice'>No active chemical agents found in [A].</span>")
else
user << "<span class='notice'>No significant chemical agents found in [A].</span>"
to_chat(user,"<span class='notice'>No significantchemical agents found in [A].</span>")
if(5)
analyze_gases(A, user)
@@ -1437,8 +1449,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
// feature to the PDA, which would better convey the availability of the feature, but this will work for now.
// Inform the user
user << "<span class='notice'>Paper scanned and OCRed to notekeeper.</span>" //concept of scanning paper copyright brainoblivion 2009
to_chat(user,"<span class='notice'>Paper scanned and OCRed to notekeeper.</span>") //concept of scanning paper copyright brainoblivion 2009
/obj/item/device/pda/proc/explode() //This needs tuning. //Sure did.
@@ -1471,7 +1482,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/list/namecounts = list()
if (toff)
usr << "Turn on your receiver in order to send messages."
to_chat(usr, "Turn on your receiver in order to send messages.")
return
for (var/obj/item/device/pda/P in PDAs)
+2 -1
View File
@@ -42,7 +42,8 @@
/obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user)
if(istype(user, /mob/living/silicon/robot))
return 1
if(user.Adjacent(target))
return 1
else
for(var/obj/item/weapon/grab/G in target.grabbed_by)
if(G.loc == user && G.state >= GRAB_AGGRESSIVE)
@@ -9,6 +9,7 @@
default_material = "wood"
force_divisor = 1.1 // 22 when wielded with weight 20 (steel)
unwielded_force_divisor = 0.7 // 15 when unwielded based on above.
dulled_divisor = 0.75 // A "dull" bat is still gonna hurt
slot_flags = SLOT_BACK
//Predefined materials go here.
@@ -94,7 +94,6 @@
icon_state = "tacknife"
item_state = "knife"
applies_material_colour = 0
unbreakable = 1
/obj/item/weapon/material/kitchen/utensil/knife/attack(target as mob, mob/living/user as mob)
if ((CLUMSY in user.mutations) && prob(50))
@@ -117,6 +116,7 @@
attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked")
default_material = "wood"
force_divisor = 0.7 // 10 when wielded with weight 15 (wood)
dulled_divisor = 0.75 // Still a club
thrown_force_divisor = 1 // as above
/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob)
@@ -33,7 +33,6 @@
name = "switchblade"
desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster."
icon_state = "switchblade"
unbreakable = 1
/obj/item/weapon/material/butterfly/boxcutter
name = "box cutter"
@@ -67,7 +66,6 @@
matter = list(DEFAULT_WALL_MATERIAL = 12000)
origin_tech = "materials=1"
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
unbreakable = 1
/obj/item/weapon/material/knife/suicide_act(mob/user)
viewers(user) << pick("<span class='danger'>\The [user] is slitting \his wrists with \the [src]! It looks like \he's trying to commit suicide.</span>", \
@@ -16,9 +16,12 @@
)
var/applies_material_colour = 1
var/unbreakable
var/unbreakable = 0 //Doesn't lose health
var/fragile = 0 //Shatters when it dies
var/dulled = 0 //Has gone dull
var/force_divisor = 0.5
var/thrown_force_divisor = 0.5
var/dulled_divisor = 0.5 //Just drops the damage by half
var/default_material = DEFAULT_WALL_MATERIAL
var/material/material
var/drops_debris = 1
@@ -47,6 +50,8 @@
else
force = material.get_blunt_damage()
force = round(force*force_divisor)
if(dulled)
force = round(force*dulled_divisor)
throwforce = round(material.get_blunt_damage()*thrown_force_divisor)
//spawn(1)
// world << "[src] has force [force] and throwforce [throwforce] when made from default material [material.name]"
@@ -77,9 +82,18 @@
health--
check_health()
/obj/item/weapon/material/attackby(obj/item/weapon/W, mob/user as mob)
if(istype(W, /obj/item/weapon/whetstone))
var/obj/item/weapon/whetstone/whet = W
repair(whet.repair_amount, whet.repair_time, user)
..()
/obj/item/weapon/material/proc/check_health(var/consumed)
if(health<=0)
shatter(consumed)
if(fragile)
shatter(consumed)
else if(!dulled)
dull()
/obj/item/weapon/material/proc/shatter(var/consumed)
var/turf/T = get_turf(src)
@@ -90,6 +104,34 @@
playsound(src, "shatter", 70, 1)
if(!consumed && drops_debris) material.place_shard(T)
qdel(src)
/obj/item/weapon/material/proc/dull()
var/turf/T = get_turf(src)
T.visible_message("<span class='danger'>\The [src] goes dull!</span>")
playsound(src, "shatter", 70, 1)
dulled = 1
if(is_sharp() || has_edge())
sharp = 0
edge = 0
/obj/item/weapon/material/proc/repair(var/repair_amount, var/repair_time, mob/living/user)
if(!fragile)
if(health < initial(health))
user.visible_message("[user] begins repairing \the [src].", "You begin repairing \the [src].")
if(do_after(user, repair_time))
user.visible_message("[user] has finished repairing \the [src]", "You finish repairing \the [src].")
health = min(health + repair_amount, initial(health))
dulled = 0
sharp = initial(sharp)
edge = initial(edge)
else
to_chat(user, "<span class='notice'>[src] doesn't need repairs.</span>")
else
to_chat(user, "<span class='warning'>You can't repair \the [src].</span>")
return
/*
Commenting this out pending rebalancing of radiation based on small objects.
/obj/item/weapon/material/process()
@@ -15,6 +15,7 @@
gender = PLURAL
w_class = ITEMSIZE_SMALL
force_divisor = 0.63
dulled_divisor = 0.75 //It's a heavy bit of metal
attack_verb = list("punched", "beaten", "struck")
applies_material_colour = 0
@@ -82,6 +83,7 @@
icon_state = "hoe"
force_divisor = 0.25 // 5 with weight 20 (steel)
thrown_force_divisor = 0.25 // as above
dulled_divisor = 0.75 //Still metal on a long pole
w_class = ITEMSIZE_SMALL
attack_verb = list("slashed", "sliced", "cut", "clawed")
@@ -85,6 +85,7 @@
desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
unwielded_force_divisor = 0.25
force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor
dulled_divisor = 0.75 //Still metal on a stick
sharp = 1
edge = 1
w_class = ITEMSIZE_LARGE
@@ -140,4 +141,5 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "poked", "jabbed", "torn", "gored")
default_material = "glass"
applies_material_colour = 0
applies_material_colour = 0
fragile = 1 //It's a haphazard thing of glass, wire, and steel
@@ -0,0 +1,11 @@
//This is in the material folder because it's used by them...
//Actual name may need to change
//All of the important code is in material_weapons.dm
/obj/item/weapon/whetstone
name = "whetstone"
desc = "A simple, fine grit stone, useful for sharpening dull edges and polishing out dents."
icon_state = "whetstone"
force = 3
w_class = ITEMSIZE_SMALL
var/repair_amount = 5
var/repair_time = 40
@@ -12,6 +12,7 @@
if(isliving(user))
if(!owner)
set_name(user.name)
to_chat(user, "[src] registers your name.")
else
to_chat(user, "[src] already has an owner!")
@@ -23,7 +23,9 @@
/obj/item/weapon/photo,
/obj/item/weapon/reagent_containers/dropper,
/obj/item/weapon/screwdriver,
/obj/item/weapon/stamp)
/obj/item/weapon/stamp,
/obj/item/weapon/permit
)
slot_flags = SLOT_ID
var/obj/item/weapon/card/id/front_id = null
+1 -1
View File
@@ -27,7 +27,7 @@
..()
/obj/structure/curtain/proc/toggle()
opacity = !opacity
set_opacity(!opacity)
if(opacity)
icon_state = "closed"
layer = SHOWER_CLOSED_LAYER
+4 -4
View File
@@ -32,9 +32,9 @@
name = "[material.display_name] door"
color = material.icon_colour
if(material.opacity < 0.5)
opacity = 0
set_opacity(0)
else
opacity = 1
set_opacity(1)
if(material.products_need_process())
processing_objects |= src
update_nearby_tiles(need_rebuild=1)
@@ -99,7 +99,7 @@
flick("[material.door_icon_base]opening",src)
sleep(10)
density = 0
opacity = 0
set_opacity(0)
state = 1
update_icon()
isSwitchingStates = 0
@@ -111,7 +111,7 @@
flick("[material.door_icon_base]closing",src)
sleep(10)
density = 1
opacity = 1
set_opacity(1)
state = 0
update_icon()
isSwitchingStates = 0
@@ -22,6 +22,9 @@
/obj/effect/wingrille_spawn/attack_generic()
activate()
/obj/effect/wingrille_spawn/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
return FALSE
/obj/effect/wingrille_spawn/initialize()
..()
if(!win_path)
+2 -2
View File
@@ -11,7 +11,7 @@
update_icon()
set_light(0)
src.blocks_air = 0
src.opacity = 0
set_opacity(0)
for(var/turf/simulated/turf in loc)
air_master.mark_for_update(turf)
else
@@ -21,7 +21,7 @@
update_icon()
set_light(1)
src.blocks_air = 1
src.opacity = 1
set_opacity(1)
for(var/turf/simulated/turf in loc)
air_master.mark_for_update(turf)
-1
View File
@@ -21,7 +21,6 @@
var/icon_old = null
var/pathweight = 1 // How much does it cost to pathfind over this turf?
var/blessed = 0 // Has the turf been blessed?
var/dynamic_lighting = 1 // Does the turf use dynamic lighting?
var/list/decals
+15 -13
View File
@@ -30,9 +30,10 @@
var/obj/fire/old_fire = fire
var/old_opacity = opacity
var/old_dynamic_lighting = dynamic_lighting
var/list/old_affecting_lights = affecting_lights
var/old_affecting_lights = affecting_lights
var/old_lighting_overlay = lighting_overlay
var/old_weather_overlay = weather_overlay
var/old_corners = corners
//world << "Replacing [src.type] with [N]"
@@ -94,15 +95,16 @@
W.post_change()
. = W
lighting_overlay = old_lighting_overlay
affecting_lights = old_affecting_lights
if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting) || force_lighting_update)
reconsider_lights()
if(dynamic_lighting != old_dynamic_lighting)
if(dynamic_lighting)
lighting_build_overlays()
else
lighting_clear_overlays()
else
if(lighting_overlay)
lighting_overlay.update_overlay()
recalc_atom_opacity()
if(lighting_overlays_initialised)
lighting_overlay = old_lighting_overlay
affecting_lights = old_affecting_lights
corners = old_corners
if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting))
reconsider_lights()
if(dynamic_lighting != old_dynamic_lighting)
if(dynamic_lighting)
lighting_build_overlay()
else
lighting_clear_overlay()
+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)
+16
View File
@@ -57,6 +57,22 @@
<h3 class="author">Anewbe updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Wooden circlets can now be worn on the head.</li>
<h2 class="date">12 April 2017</h2>
<h3 class="author">Anewbe updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Material weapons now go dull instead of shattering. Certain weapons, like spears, will still shatter.</li>
<li class="rscadd">Dull weapons do less damage, but can be sharpened with a whetstone.</li>
<li class="rscadd">Whetstones can be crafted using plasteel.</li>
</ul>
<h3 class="author">Belsima updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">Replaces cypherkey sprites with improved ones.</li>
</ul>
<h3 class="author">Sin4 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">You can no longer ascend a table by walking from a flipped table to a non-flipped one.</li>
</ul>
<h2 class="date">08 April 2017</h2>
<h3 class="author">Anewbe updated:</h3>
<ul class="changes bgimages16">
+11
View File
@@ -3399,3 +3399,14 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
Sin4:
- bugfix: Fixed back icons for Z8 Carbine, C20r, fireaxe, spear, and baseball bat.
- rscadd: If observing before roundstart, you may respawn.
2017-04-12:
Anewbe:
- rscadd: Material weapons now go dull instead of shattering. Certain weapons, like
spears, will still shatter.
- rscadd: Dull weapons do less damage, but can be sharpened with a whetstone.
- rscadd: Whetstones can be crafted using plasteel.
Belsima:
- tweak: Replaces cypherkey sprites with improved ones.
Sin4:
- bugfix: You can no longer ascend a table by walking from a flipped table to a
non-flipped one.
@@ -0,0 +1,36 @@
################################
# Example Changelog File
#
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
#
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
# When it is, any changes listed below will disappear.
#
# Valid Prefixes:
# bugfix
# wip (For works in progress)
# tweak
# soundadd
# sounddel
# rscadd (general adding of nice things)
# rscdel (general deleting of nice things)
# imageadd
# imagedel
# maptweak
# spellcheck (typo fixes)
# experiment
#################################
# Your name.
author: Anewbe
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
changes:
- rscadd: "Bartenders now spawn with their shotgun permit. Click on it in hand to name it."
+37
View File
@@ -0,0 +1,37 @@
################################
# Example Changelog File
#
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
#
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
# When it is, any changes listed below will disappear.
#
# Valid Prefixes:
# bugfix
# wip (For works in progress)
# tweak
# soundadd
# sounddel
# rscadd (general adding of nice things)
# rscdel (general deleting of nice things)
# imageadd
# imagedel
# maptweak
# spellcheck (typo fixes)
# experiment
#################################
# Your name.
author: Anewbe
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
changes:
- rscadd: "Hardhats now give some ear protection."
- rscdel: "Hardhats can no longer fit in pockets."
@@ -22,7 +22,7 @@
#################################
# Your name.
author: Belsima
author: LorenLuke
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
@@ -33,4 +33,4 @@ delete-after: True
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
changes:
- tweak: "Replaces cypherkey sprites with improved ones."
- rscadd: "Allows removal of PDA ID with alt-click."
+38
View File
@@ -0,0 +1,38 @@
################################
# Example Changelog File
#
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
#
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
# When it is, any changes listed below will disappear.
#
# Valid Prefixes:
# bugfix
# wip (For works in progress)
# tweak
# soundadd
# sounddel
# rscadd (general adding of nice things)
# rscdel (general deleting of nice things)
# imageadd
# imagedel
# maptweak
# spellcheck (typo fixes)
# experiment
#################################
# Your name.
author: Yosh
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
changes:
- tweak: "Scrubbers now scrub Phoron by default."
- tweak: "Scrubbers now have the first dangerzone at anything more than 0 Phoron."
- bugfix: "No longer will you attack Alarms with your ID when trying to unlock them."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 132 KiB

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