From cebbac32e808f560d79da9fa729569bfccbd4938 Mon Sep 17 00:00:00 2001 From: LorenLuke Date: Tue, 11 Apr 2017 00:25:29 -0700 Subject: [PATCH 01/18] Allows usage of ctrl+numpad for target zone selecting. --- code/modules/mob/mob.dm | 44 +++++++++++++++++++++++++++++++++++++++++ interface/skin.dmf | 28 ++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 57ad0859e1..3e0bbcd9ff 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1017,3 +1017,47 @@ mob/proc/yank_out_object() /mob/proc/is_muzzled() return 0 + +/client/proc/check_has_body_select() + return mob && mob.hud_used && istype(mob.zone_sel, /obj/screen/zone_sel) + +/client/verb/body_toggle_head() + set name = "body-toggle-head" + set hidden = 1 + toggle_zone_sel(list(BP_HEAD,BP_EYES,BP_MOUTH)) + +/client/verb/body_r_arm() + set name = "body-r-arm" + set hidden = 1 + toggle_zone_sel(list(BP_R_ARM,BP_R_HAND)) + +/client/verb/body_l_arm() + set name = "body-l-arm" + set hidden = 1 + toggle_zone_sel(list(BP_L_ARM,BP_L_HAND)) + +/client/verb/body_chest() + set name = "body-chest" + set hidden = 1 + toggle_zone_sel(list(BP_CHEST)) + +/client/verb/body_groin() + set name = "body-groin" + set hidden = 1 + toggle_zone_sel(list(BP_GROIN)) + +/client/verb/body_r_leg() + set name = "body-r-leg" + set hidden = 1 + toggle_zone_sel(list(BP_R_LEG,BP_R_FOOT)) + +/client/verb/body_l_leg() + set name = "body-l-leg" + set hidden = 1 + toggle_zone_sel(list(BP_L_LEG,BP_L_FOOT)) + +/client/proc/toggle_zone_sel(list/zones) + if(!check_has_body_select()) + return + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(next_in_list(mob.zone_sel.selecting,zones)) \ No newline at end of file diff --git a/interface/skin.dmf b/interface/skin.dmf index be23750148..2440677224 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -409,6 +409,34 @@ macro "macro" name = "CTRL+Z" command = "Activate-Held-Object" is-disabled = false + elem + name = "CTRL+NUMPAD1" + command = "body-r-leg" + is-disabled = false + elem + name = "CTRL+NUMPAD2" + command = "body-groin" + is-disabled = false + elem + name = "CTRL+NUMPAD3" + command = "body-l-leg" + is-disabled = false + elem + name = "CTRL+NUMPAD4" + command = "body-r-arm" + is-disabled = false + elem + name = "CTRL+NUMPAD5" + command = "body-chest" + is-disabled = false + elem + name = "CTRL+NUMPAD6" + command = "body-l-arm" + is-disabled = false + elem + name = "CTRL+NUMPAD8" + command = "body-toggle-head" + is-disabled = false elem name = "F1" command = "adminhelp" From 69a8d84344667992e3baa400c9863d02d10a748f Mon Sep 17 00:00:00 2001 From: LorenLuke Date: Tue, 11 Apr 2017 00:39:21 -0700 Subject: [PATCH 02/18] Fixes a few errors. --- code/_onclick/hud/screen_objects.dm | 6 ++++++ code/modules/mob/mob.dm | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 3f6758c6ad..e55323fb3a 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -162,6 +162,12 @@ update_icon() return 1 +/obj/screen/zone_sel/proc/set_selected_zone(bodypart) + var/old_selecting = selecting + selecting = bodypart + if(old_selecting != selecting) + update_icon() + /obj/screen/zone_sel/update_icon() overlays.Cut() overlays += image('icons/mob/zone_sel.dmi', "[selecting]") diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 3e0bbcd9ff..714bde7096 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1024,7 +1024,7 @@ mob/proc/yank_out_object() /client/verb/body_toggle_head() set name = "body-toggle-head" set hidden = 1 - toggle_zone_sel(list(BP_HEAD,BP_EYES,BP_MOUTH)) + toggle_zone_sel(list(BP_HEAD, O_EYES, O_MOUTH)) /client/verb/body_r_arm() set name = "body-r-arm" @@ -1039,7 +1039,7 @@ mob/proc/yank_out_object() /client/verb/body_chest() set name = "body-chest" set hidden = 1 - toggle_zone_sel(list(BP_CHEST)) + toggle_zone_sel(list(BP_TORSO)) /client/verb/body_groin() set name = "body-groin" From 8a2c84eba33067303c673b68ff6bbdfa5ae57182 Mon Sep 17 00:00:00 2001 From: Anewbe Date: Tue, 11 Apr 2017 12:25:48 -0500 Subject: [PATCH 03/18] Weakens bloodloss from burn damage --- code/modules/organs/organ_external.dm | 2 +- html/changelogs/Anewbe - Burn Bloodloss.yml | 36 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 html/changelogs/Anewbe - Burn Bloodloss.yml diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 4034346fdd..63c6da6409 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -492,7 +492,7 @@ This function completely restores a damaged organ to perfect condition. //Burn damage can cause fluid loss due to blistering and cook-off if((damage > 5 || damage + burn_dam >= 15) && type == BURN && (robotic < ORGAN_ROBOT)) - var/fluid_loss = (damage/(owner.maxHealth - config.health_threshold_dead)) * owner.species.blood_volume*(1 - BLOOD_VOLUME_SURVIVE/100) + var/fluid_loss = 0.75 * (damage/(owner.maxHealth - config.health_threshold_dead)) * owner.species.blood_volume*(1 - BLOOD_VOLUME_SURVIVE/100) owner.remove_blood(fluid_loss) // first check whether we can widen an existing wound diff --git a/html/changelogs/Anewbe - Burn Bloodloss.yml b/html/changelogs/Anewbe - Burn Bloodloss.yml new file mode 100644 index 0000000000..512606ca80 --- /dev/null +++ b/html/changelogs/Anewbe - Burn Bloodloss.yml @@ -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: + - experiment: "Lessens the bloodloss from severe burn damage." \ No newline at end of file From 45058136e2e5a62988c3f18d460758b0fd2f4055 Mon Sep 17 00:00:00 2001 From: Leshana Date: Tue, 11 Apr 2017 00:30:49 -0400 Subject: [PATCH 04/18] Replace `opacity = X` with `set_opacity(X)` so that the lighting system can update propertly. --- code/game/objects/effects/chem/chemsmoke.dm | 4 ++-- code/game/objects/structures/curtains.dm | 2 +- code/game/objects/structures/simple_doors.dm | 8 ++++---- code/game/turfs/simulated/wall_attacks.dm | 4 ++-- code/modules/hydroponics/spreading/spreading.dm | 2 +- code/modules/random_map/drop/droppod_doors.dm | 4 ++-- code/modules/shieldgen/emergency_shield.dm | 12 ++++++------ 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index 3fa93e7e46..3f077116e9 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -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) diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index bd241bb63a..a2fdfd898d 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -27,7 +27,7 @@ ..() /obj/structure/curtain/proc/toggle() - opacity = !opacity + set_opacity(!opacity) if(opacity) icon_state = "closed" layer = SHOWER_CLOSED_LAYER diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 10fa149e8b..58742c3b60 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -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 diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 71c17b5895..47620d030a 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -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) diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 48d2c542dd..1cff4314af 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -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 diff --git a/code/modules/random_map/drop/droppod_doors.dm b/code/modules/random_map/drop/droppod_doors.dm index eb5b7c3e17..7009a5885b 100644 --- a/code/modules/random_map/drop/droppod_doors.dm +++ b/code/modules/random_map/drop/droppod_doors.dm @@ -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" \ No newline at end of file diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 3690a1f2ba..f7bc1d1c80 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -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 From e60c5523966dfab0bfd32a1cbba1c74e065e9c0a Mon Sep 17 00:00:00 2001 From: LorenLuke Date: Wed, 12 Apr 2017 03:11:43 -0700 Subject: [PATCH 05/18] Adds ability to not just chat-mode. --- interface/skin.dmf | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/interface/skin.dmf b/interface/skin.dmf index 2440677224..7c92a788c1 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -215,6 +215,34 @@ macro "borghotkeymode" name = "CTRL+Z" command = "Activate-Held-Object" is-disabled = false + elem + name = "NUMPAD1" + command = "body-r-leg" + is-disabled = false + elem + name = "NUMPAD2" + command = "body-groin" + is-disabled = false + elem + name = "NUMPAD3" + command = "body-l-leg" + is-disabled = false + elem + name = "NUMPAD4" + command = "body-r-arm" + is-disabled = false + elem + name = "NUMPAD5" + command = "body-chest" + is-disabled = false + elem + name = "NUMPAD6" + command = "body-l-arm" + is-disabled = false + elem + name = "NUMPAD8" + command = "body-toggle-head" + is-disabled = false elem name = "F1" command = "adminhelp" @@ -719,6 +747,34 @@ macro "hotkeymode" name = "CTRL+Z" command = "Activate-Held-Object" is-disabled = false + elem + name = "NUMPAD1" + command = "body-r-leg" + is-disabled = false + elem + name = "NUMPAD2" + command = "body-groin" + is-disabled = false + elem + name = "NUMPAD3" + command = "body-l-leg" + is-disabled = false + elem + name = "NUMPAD4" + command = "body-r-arm" + is-disabled = false + elem + name = "NUMPAD5" + command = "body-chest" + is-disabled = false + elem + name = "NUMPAD6" + command = "body-l-arm" + is-disabled = false + elem + name = "NUMPAD8" + command = "body-toggle-head" + is-disabled = false elem name = "F1" command = "adminhelp" @@ -909,6 +965,34 @@ macro "borgmacro" name = "CTRL+Z" command = "Activate-Held-Object" is-disabled = false + elem + name = "CTRL+NUMPAD1" + command = "body-r-leg" + is-disabled = false + elem + name = "CTRL+NUMPAD2" + command = "body-groin" + is-disabled = false + elem + name = "CTRL+NUMPAD3" + command = "body-l-leg" + is-disabled = false + elem + name = "CTRL+NUMPAD4" + command = "body-r-arm" + is-disabled = false + elem + name = "CTRL+NUMPAD5" + command = "body-chest" + is-disabled = false + elem + name = "CTRL+NUMPAD6" + command = "body-l-arm" + is-disabled = false + elem + name = "CTRL+NUMPAD8" + command = "body-head-toggle" + is-disabled = false elem name = "F1" command = "adminhelp" From e8af4afc88103672be4119c8e6bf06e5f47cce04 Mon Sep 17 00:00:00 2001 From: Leshana Date: Tue, 11 Apr 2017 22:42:24 -0400 Subject: [PATCH 06/18] Port tg/paradise/good soft-edge lighting Ports https://github.com/ParadiseSS13/Paradise/pull/6161 Its merged in nearly verbatim as far as the lighting module goes. Changes outside the lighting module are merged based on our codebase. --- code/__defines/lighting.dm | 44 +++ code/_helpers/unsorted.dm | 2 +- code/controllers/Processes/lighting.dm | 98 ++++++ code/game/area/Space Station 13 areas.dm | 32 +- code/game/area/areas.dm | 2 +- code/game/area/asteroid_areas.dm | 2 +- code/game/atoms_movable.dm | 6 + code/game/gamemodes/cult/hell_universe.dm | 2 +- .../endgame/supermatter_cascade/universe.dm | 2 +- code/game/turfs/turf.dm | 1 - code/game/turfs/turf_changing.dm | 28 +- code/modules/lighting/__lighting_docs.dm | 8 +- code/modules/lighting/_lighting_defs.dm | 9 - code/modules/lighting/light_source.dm | 300 ------------------ code/modules/lighting/lighting_area.dm | 9 + code/modules/lighting/lighting_atom.dm | 47 ++- code/modules/lighting/lighting_corner.dm | 125 ++++++++ code/modules/lighting/lighting_overlay.dm | 127 ++++---- code/modules/lighting/lighting_process.dm | 35 -- code/modules/lighting/lighting_setup.dm | 16 + code/modules/lighting/lighting_source.dm | 290 +++++++++++++++++ code/modules/lighting/lighting_system.dm | 25 -- code/modules/lighting/lighting_turf.dm | 100 +++++- code/modules/lighting/~lighting_undefs.dm | 6 +- code/modules/mining/mine_turfs.dm | 1 + .../reagent_containers/food/drinks/bottle.dm | 2 +- icons/effects/lighting_overlay.dmi | Bin 260 -> 0 bytes icons/effects/lighting_overlay.png | Bin 0 -> 1480 bytes maps/RandomZLevels/zresearchlabs.dmm | 118 +++---- maps/overmap/bearcat/bearcat.dm | 2 +- polaris.dme | 9 +- 31 files changed, 876 insertions(+), 572 deletions(-) create mode 100644 code/controllers/Processes/lighting.dm delete mode 100644 code/modules/lighting/_lighting_defs.dm delete mode 100644 code/modules/lighting/light_source.dm create mode 100644 code/modules/lighting/lighting_area.dm create mode 100644 code/modules/lighting/lighting_corner.dm delete mode 100644 code/modules/lighting/lighting_process.dm create mode 100644 code/modules/lighting/lighting_setup.dm create mode 100644 code/modules/lighting/lighting_source.dm delete mode 100644 code/modules/lighting/lighting_system.dm delete mode 100644 icons/effects/lighting_overlay.dmi create mode 100644 icons/effects/lighting_overlay.png diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index 56c95535fe..58f2e96b14 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -4,3 +4,47 @@ 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.png' // icon used for lighting shading effects + +#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_SOFT_THRESHOLD 0.05 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. + +// 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 diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index d244916ec2..0fa1b9033b 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -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 diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm new file mode 100644 index 0000000000..d46cab1d19 --- /dev/null +++ b/code/controllers/Processes/lighting.dm @@ -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], " | ")]") diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 0c8d6ee052..01e95e5716 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -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" @@ -2685,7 +2685,7 @@ area/space/atmosalert() name = "Beach" icon_state = "null" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 requires_power = 0 ambience = list() var/sound/mysound = null @@ -2807,7 +2807,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 /* diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index e118e6fa4d..f152050990 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -18,7 +18,7 @@ power_equip = 0 power_environ = 0 - if(lighting_use_dynamic) + if(dynamic_lighting) luminosity = 0 else luminosity = 1 diff --git a/code/game/area/asteroid_areas.dm b/code/game/area/asteroid_areas.dm index 68f9807ead..66a7e86913 100644 --- a/code/game/area/asteroid_areas.dm +++ b/code/game/area/asteroid_areas.dm @@ -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" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index b76620fa95..b511c9befd 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -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 diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index 496af98ef8..75c97d5cee 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -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) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index 11c61cfed6..6f549d15f4 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -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 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 1074af1b90..c13b4606d3 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -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 diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 7379d2c80b..78946ff832 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -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() \ No newline at end of file + 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() diff --git a/code/modules/lighting/__lighting_docs.dm b/code/modules/lighting/__lighting_docs.dm index 5ac5897eb5..8ef93935e9 100644 --- a/code/modules/lighting/__lighting_docs.dm +++ b/code/modules/lighting/__lighting_docs.dm @@ -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 -*/ +*/ \ No newline at end of file diff --git a/code/modules/lighting/_lighting_defs.dm b/code/modules/lighting/_lighting_defs.dm deleted file mode 100644 index 21fdafdaeb..0000000000 --- a/code/modules/lighting/_lighting_defs.dm +++ /dev/null @@ -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 diff --git a/code/modules/lighting/light_source.dm b/code/modules/lighting/light_source.dm deleted file mode 100644 index 3675cd945c..0000000000 --- a/code/modules/lighting/light_source.dm +++ /dev/null @@ -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 diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm new file mode 100644 index 0000000000..92923e6b0d --- /dev/null +++ b/code/modules/lighting/lighting_area.dm @@ -0,0 +1,9 @@ +/area + luminosity = TRUE + var/dynamic_lighting = TRUE + +/area/New() + . = ..() + + if(dynamic_lighting) + luminosity = FALSE \ No newline at end of file diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index c900f17bf8..6ec1e6ecd7 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -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)) + 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() . = ..() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm new file mode 100644 index 0000000000..907e7aab8b --- /dev/null +++ b/code/modules/lighting/lighting_corner.dm @@ -0,0 +1,125 @@ +/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/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 + + else if (mx < LIGHTING_SOFT_THRESHOLD) + . = 0 // 0 means soft lighting. + + cache_r = lum_r * . || LIGHTING_SOFT_THRESHOLD + cache_g = lum_g * . || LIGHTING_SOFT_THRESHOLD + cache_b = lum_b * . || LIGHTING_SOFT_THRESHOLD + cache_mx = mx + + 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 diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 2dd3889e4e..4679d1fafc 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -1,102 +1,89 @@ +/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 + color = list( + cr.cache_r, cr.cache_g, cr.cache_b, 0, + cg.cache_r, cg.cache_g, cg.cache_b, 0, + cb.cache_r, cb.cache_g, cb.cache_b, 0, + ca.cache_r, ca.cache_g, ca.cache_b, 0, + 0, 0, 0, 1 + ) + luminosity = max > LIGHTING_SOFT_THRESHOLD + + + +/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 ..() diff --git a/code/modules/lighting/lighting_process.dm b/code/modules/lighting/lighting_process.dm deleted file mode 100644 index 055e366337..0000000000 --- a/code/modules/lighting/lighting_process.dm +++ /dev/null @@ -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 diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm new file mode 100644 index 0000000000..981de65864 --- /dev/null +++ b/code/modules/lighting/lighting_setup.dm @@ -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) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm new file mode 100644 index 0000000000..d878d8c4b6 --- /dev/null +++ b/code/modules/lighting/lighting_source.dm @@ -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 diff --git a/code/modules/lighting/lighting_system.dm b/code/modules/lighting/lighting_system.dm deleted file mode 100644 index 0c84294f35..0000000000 --- a/code/modules/lighting/lighting_system.dm +++ /dev/null @@ -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 diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index f8f0a5ab10..cc9c678b97 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -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]) diff --git a/code/modules/lighting/~lighting_undefs.dm b/code/modules/lighting/~lighting_undefs.dm index 772f70557a..2f863b73d5 100644 --- a/code/modules/lighting/~lighting_undefs.dm +++ b/code/modules/lighting/~lighting_undefs.dm @@ -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 diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index f46d384f86..cc7c8c40bb 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -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) + // TODO - Look into this reconsider_lights() if(air_master) air_master.mark_for_update(src) diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index 0045769d74..3300eb91d6 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm @@ -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) diff --git a/icons/effects/lighting_overlay.dmi b/icons/effects/lighting_overlay.dmi deleted file mode 100644 index 1ff16d2b78b6a89fd525856cfee87830095ec9c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^2|(1VBG)! diff --git a/icons/effects/lighting_overlay.png b/icons/effects/lighting_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..b317268b702d50c33c0099a63d0a0ea94a607744 GIT binary patch literal 1480 zcmV;(1vmPMP);Uvh8NW&Hj9u{oJD8zyJ38|NQOa z|M-u;{hjmky?xmq8}>77#ec@IUtGUa@plf}ajk^;9)r~m<`{nLsNF2OdGx_rY3A96 zWwR9@KGZP&dj?k!_@MxP`89$47XoiqfPb^<8{l_T-46&HRgVq^@fTWi|u4@}5K=9BMP`7XFfwUSsJI_mEWW(PCeh02& zNn|@X0Zjqdt-wdi7E1uvIVX_Dg_zyjxLsR=04I?4)&vMoe@{5NZ|_WYuexV8bdMw_ z&@_QA0ZbqPZ4k(UySG5!=(=I=@@j&;6`0+l<|&{l_`4(sME8`S0Kk+83glVQJ(ocO zgw_Q7g1fb#04kw5DB*&Sq{JmqVAgR4YfH&VwI zYSle5C?OFFa1Vs-P4 zXaf5TCA8^OIRy$ZfTMH3Id(`;0M{oRQ6a0nnn6DWZw0pHIYOQO{TB!C=9 zTLKlqmF*74#$&b#?*-3-r*GJ505g{WM~@J`AxAr~0ShX4KLp0_3qGL>e&9&E;C;iX z-D50?+%U|ZnOjNN23#K{z)g7YNAQCZ*;xVZ5IMN!+HC9|Y74&Y60i*?vEU^!2%q4_ zD)=aPh21}FL*Lt+z;XWUgYXr~0IqNl3VK2`+F=;5gy-I3)xhkE$9y^Qd}G-~rq=C~%w;0NlD`C$y3P7zx59flY=S zvDRK|e!?$kRjfClT@`z|psa^u_)hdgOK@lp?6n~^f#y*H9t87rV-1~fTxZ_rA8US0 zXw`fKps}s4QM*4<0_eK48YZA!@W6iM1lakE;JctYTC>19WTNhEopcxcHuhB5wIn*O z0|HxnVpU6`Yy1HFhXA^7E0FL?@=R~*eDBIUV7Sxc-{H^D_=E6gPB5ncI7HBW<*6y0 z0@gHvJOyxc=6bLY>hpLVXZLBJYaTNh@x-Z(3B*$q=oEoKqk!_jbMR@r8QUdbf}1;^ zTodk9*F$BUa5`kV?(vqJI&U3aZzX{1fP}z+ati22*AyUpTmsyL=$zn!U&-}IJ)dfl z5SX_Zg7*M+FOlrp`2aDE-9KZ31z!_rl<=3PP!nMHt_h4w01ps=^XwkaaUHAce80dA zw^|FrYZ47(_bGv(``1_k?ypf3P`w1uJ>L@-9Ot#`Io<%z^+;V$HNo*JnsvXoz`OCr zO4x!wMpeNv%onEcLZ1|Q0d=2%Q-E(ulz>3JFb;J;_+m{c-}G2{u6K5>^W6gz2*8+{ z0Q?sm9V-()!|ov>3D+Jd@cFhnZf~$DfhEyZ0Ua3y_o_-e1g^AOZg6M!EA|C}FMX8o zCDkqouD8JEHvvn2a%}qk@#-F*A*>I48$GxTgVjpFX7`e?w%}hty};lUZUXohQ}<=U z?)l+>7YR!A`nCy4SbeJjr|xgm*~ z0>LZw1qdj!X#?W(6zLP(pGLUAt-yv`&ua6rd%;WKTOX!&&jPP++HrpG-lDE=>5qS= iJ#V4J*JlhL?0*5J6wAQ~73C2C0000 Date: Tue, 11 Apr 2017 20:14:05 -0400 Subject: [PATCH 07/18] Replace direct access of lighting overlays with T.get_lumcount() --- .../technomancer/spells/summon/summon_ward.dm | 7 ++----- code/modules/hydroponics/seed.dm | 6 +----- code/modules/hydroponics/trays/tray.dm | 7 +------ .../integrated_electronics/passive/power.dm | 7 ++----- .../modules/mob/living/carbon/alien/diona/life.dm | 7 +------ code/modules/mob/living/carbon/human/life.dm | 15 +++------------ 6 files changed, 10 insertions(+), 39 deletions(-) diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm index 5bb032b9c7..c6e5a2804d 100644 --- a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm +++ b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm @@ -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. diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 1d92705dec..010888602f 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -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 diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index ddcde8a2b1..efdae46db7 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -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" diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm index 9e08a96d24..2b8c200a27 100644 --- a/code/modules/integrated_electronics/passive/power.dm +++ b/code/modules/integrated_electronics/passive/power.dm @@ -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) diff --git a/code/modules/mob/living/carbon/alien/diona/life.dm b/code/modules/mob/living/carbon/alien/diona/life.dm index e7fdc43dbc..fecf9b12c0 100644 --- a/code/modules/mob/living/carbon/alien/diona/life.dm +++ b/code/modules/mob/living/carbon/alien/diona/life.dm @@ -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 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index af360bd829..e6563a28a1 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -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 @@ -1358,8 +1350,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() From 5f629e2aca9c26aac986b0cd85ad1a3a26560ea4 Mon Sep 17 00:00:00 2001 From: Leshana Date: Tue, 11 Apr 2017 22:23:16 -0400 Subject: [PATCH 08/18] Optimizes lighting overlays for slow clients. Ports https://github.com/tgstation/tgstation/pull/25778 and https://github.com/tgstation/tgstation/pull/25861 * Instead of using color matrix for all overlays, fully dark and fully light use static black and transparant icon states without color matrix. * Also implements LIGHTING_ROUND_VALUE so that lighting values actually will reach 0 or 1. --- code/__defines/lighting.dm | 4 +- code/modules/lighting/lighting_corner.dm | 18 +++++--- code/modules/lighting/lighting_overlay.dm | 50 +++++++++++++++++++--- icons/effects/lighting_overlay.dmi | Bin 0 -> 1882 bytes 4 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 icons/effects/lighting_overlay.dmi diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index 58f2e96b14..0b581cf780 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -10,9 +10,9 @@ #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.png' // icon used for lighting shading effects +#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects -#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_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. diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 907e7aab8b..63ce123253 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -98,20 +98,28 @@ 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 = lum_r * . || LIGHTING_SOFT_THRESHOLD - cache_g = lum_g * . || LIGHTING_SOFT_THRESHOLD - cache_b = lum_b * . || LIGHTING_SOFT_THRESHOLD - cache_mx = mx + 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 diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 4679d1fafc..559e3a87f8 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -59,16 +59,52 @@ var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx) - color = list( - cr.cache_r, cr.cache_g, cr.cache_b, 0, - cg.cache_r, cg.cache_g, cg.cache_b, 0, - cb.cache_r, cb.cache_g, cb.cache_b, 0, - ca.cache_r, ca.cache_g, ca.cache_b, 0, - 0, 0, 0, 1 + 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 = "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 = max > LIGHTING_SOFT_THRESHOLD + 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 diff --git a/icons/effects/lighting_overlay.dmi b/icons/effects/lighting_overlay.dmi new file mode 100644 index 0000000000000000000000000000000000000000..51985c0c981c5bdb3dd57606b733474016ca5c5d GIT binary patch literal 1882 zcmb7_`#;l*AICqt$QWYCWUdbh6Pux}IHo2vw~X8`8A4h^n24EMN1|_$)%JDNBvS5< zaa1GQiR02a5_O7CE)_YuX~!)cjrYniW5&mU2aJ<^Et^}O+N7h4+S##Ry?+#2M++d%H1eVzAXMfH+!z- z5>XUFz47AKcIjE2I_q@Y9=m1s|oogTItkX(FuuZ#2(x|k~~hm7LL6V^m{F! zSpYp9yVoQF*o6ROodF`41O}>0M!``DLuxitXS*b~)Gona!H*M1`Re@9+7*;22&4kx zp#90O-qJv*v~44AqiwvoC-kRW!_Oaq66+5($AY2pT*A=aV|VKStb2akX28Yg!1#W^ z6H@UKC6W)5V&!F@y?}LCW*Mo5eHjs zSt7@|iG54I6RXj4=zS&gXtQD8LFS+a*2ugK>^Cjq90kNQC_RUVgKnZBy`%3R8~0g? z8DiSBrT8k{N^JT_LJ}t;YP}z*fco}oa)p<0sx7?{qMCe}F$9U3Uh1e7Qk$viM@oH) z>(F&*zh+O!oYFBlfzZfW5#eNriy7ut+t5utA=vAretuL& zp%oFykk7Pa9bh#(xofDelAyQ{bR~v?sl@w*Jl8x*IPF}*UEw%ZdC3&o9Ayd7Q5$7d zCeFv>snA=<`4LrVZn(R1h6_UxD9+r@R-f|p{3?`&~;Z->)+q;ut<+`AJ z%-HULw5EM?D8FEh1SL9<96V**0hoQHdD6-5oJJL{73)g{{I~MABHk{erL;HVb9Q<{UtR~Y0O~MgxbW_kC#T&08)TKbfq+8X2Uyq@}1$C zx9@E8g91bU6BXE}(B^S?)?IYn!<~jr8_Do*pq5(>HtX`R%j4fRLCp@9H za6WIeDG(hNz2(Q|Tjnk=&*M&hyD@-=C=e9n1ihw+Gw*zEh6~dF+r9rxmhwI5XvV!Z literal 0 HcmV?d00001 From d00c2596e8f91b0b6c07d1b435368960f97d4a84 Mon Sep 17 00:00:00 2001 From: Leshana Date: Wed, 12 Apr 2017 21:43:07 -0400 Subject: [PATCH 09/18] Restores "soft" darksight vision. The previous client-side optimization made lighting overlays go fully opaque when it was dark, making it so species darksight didn't let you see anything. This fixes that by making a dark_soft icon state that has opacity matching the LIGHTING_SOFT_THRESHOLD. --- code/__defines/lighting.dm | 5 +++-- code/modules/lighting/lighting_overlay.dm | 2 +- icons/effects/lighting_overlay.dmi | Bin 1882 -> 1890 bytes 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index 0b581cf780..f311ea4aa2 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -11,10 +11,11 @@ #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_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. +#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 \ diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 559e3a87f8..d46c5461df 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -88,7 +88,7 @@ icon_state = "transparent" color = null else if(!set_luminosity) - icon_state = "dark" + icon_state = LIGHTING_ICON_STATE_DARK color = null else icon_state = null diff --git a/icons/effects/lighting_overlay.dmi b/icons/effects/lighting_overlay.dmi index 51985c0c981c5bdb3dd57606b733474016ca5c5d..b8c184562f99e39f017361f25bbd1f47cb5039e9 100644 GIT binary patch delta 1792 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 U5BpPvh5!Hn07*qoM6N<$f;s~}%m4rY From 1b3a5e8c7a18c22cebf57af06f03435b9be39a74 Mon Sep 17 00:00:00 2001 From: killer653 Date: Fri, 14 Apr 2017 15:06:23 -0400 Subject: [PATCH 10/18] Makes it so xenoarch soulstone has a sprite Xenaorch soulstones would become an invisible sprite when used due to lacking the "soulstone2" sprite. This fixes that. --- icons/obj/xenoarchaeology.dmi | Bin 64235 -> 63847 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/xenoarchaeology.dmi b/icons/obj/xenoarchaeology.dmi index 4ec3da1adc25484cc28864925f17448ee6be55fe..a074f17c3f9cf368da35947f86207a09870dad38 100644 GIT binary patch delta 42151 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 delta 42525 zcmZttbyQT}_rQ&VgoJ>AG)Sj(H%KGWokL1@*IY!TQ@R_J?q)zzx;sX?MH+^B#`ov@ z{PByk)~vb1IrqfA`|SPN`yTerk&dpA;^~C{_-W~R$XK~sxY@dR*g88QAoyez>nS_0 zaJ~{7?2@wKAftMf@-Vdi7)*fStH=UU`Zt!Nq)HWmA4Uv6ooZ8Ny7RiLqwFEm| zs?qt3v>0Qtxyt@rk`nv%t6C})$I$S_*Fw3-XEHbQ*bYC33w}lN(E1f@n&=GsyExPF z`B$*ACmJKt0yYl$r-fn*1`^ow^(cuXsdr~uHiPU&*1nA!lkc|9+<7mDeH6b~@#@9) z^en#DnoaO{VPn-S$Rzls-ggJdC(ipbf1$S-*1%R@36GckU+&}2KGfHe9dG=pOkUZ% zpq7MxgyA1vn-^T{4JdYNob{CvrndOB{!C! zq33PH{uL^DPBR6z>Kb;KwezJ)dMIa1uV4^Kqga)Kc;^pghwPW`LXP4Q1YF14al#^xda_awQkFid|q1Lldb`~vN^@v64*a927(MDzYBRG-R)67(gfkue3@!jyW2@# z$i}$`l)O8WJ(cHJ7J`(zmLN~DOHqo3XJ1dIhV|TjqHxd}Pa?hgox+{L2|E|sFQe_4 z^PIyqIcz&bJM}R3yyYeHnA@1~o74_dJ$kRZR;P`6S_S`h+v!={oH1W$)LPZhHgTqr z-Y-|0v#%a#La$ZK%DMC(MoC2>>i8Wzl7vYK_A-nJq^AOH%0AflvH3q-b-y*qN$eUJ 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 Date: Fri, 14 Apr 2017 21:33:37 -0400 Subject: [PATCH 11/18] Make planets more generic So that Sif doesn't have to be the only one! --- code/controllers/Processes/planet.dm | 9 +++++++-- code/modules/admin/verbs/debug.dm | 4 ++-- code/modules/planet/planet.dm | 13 +++++++++---- code/modules/planet/sif.dm | 18 +++++++++--------- code/modules/planet/time.dm | 4 ---- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/code/controllers/Processes/planet.dm b/code/controllers/Processes/planet.dm index 31406e92a0..b3b556d7bd 100644 --- a/code/controllers/Processes/planet.dm +++ b/code/controllers/Processes/planet.dm @@ -1,11 +1,16 @@ +var/datum/controller/process/planet/planet_controller = null + /datum/controller/process/planet var/list/planets = list() /datum/controller/process/planet/setup() name = "planet" + planet_controller = src schedule_interval = 600 // every minute - planet_sif = new() - planets.Add(planet_sif) + var/list/planet_datums = typesof(/datum/planet) - /datum/planet + for(var/P in planet_datums) + var/datum/planet/NP = new P() + planets.Add(NP) /datum/controller/process/planet/doWork() for(var/datum/planet/P in planets) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 5628b8774f..f06ab9ca33 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -977,7 +977,7 @@ if(!check_rights(R_DEBUG)) return - var/datum/planet/planet = input(usr, "Which planet do you want to modify the weather on?", "Change Weather") in list(planet_sif) + var/datum/planet/planet = input(usr, "Which planet do you want to modify the weather on?", "Change Weather") in planet_controller.planets var/datum/weather/new_weather = input(usr, "What weather do you want to change to?", "Change Weather") as null|anything in planet.weather_holder.allowed_weather_types if(new_weather) planet.weather_holder.change_weather(new_weather) @@ -993,7 +993,7 @@ if(!check_rights(R_DEBUG)) return - var/datum/planet/planet = input(usr, "Which planet do you want to modify time on?", "Change Time") in list(planet_sif) + var/datum/planet/planet = input(usr, "Which planet do you want to modify time on?", "Change Time") in planet_controller.planets var/datum/time/current_time_datum = planet.current_time var/new_hour = input(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh"))) as null|num diff --git a/code/modules/planet/planet.dm b/code/modules/planet/planet.dm index 65a2de9845..53a07f228b 100644 --- a/code/modules/planet/planet.dm +++ b/code/modules/planet/planet.dm @@ -36,7 +36,12 @@ if(weather_holder) weather_holder.process() -// Returns the time datum of Sif. -/proc/get_sif_time() - if(planet_sif) - return planet_sif.current_time \ No newline at end of file +/datum/planet/proc/update_sun_deferred(var/new_range, var/new_brightness, var/new_color) + set background = 1 + set waitfor = 0 + var/i = 0 + for(var/turf/simulated/floor/T in outdoor_turfs) + T.set_light(new_range, new_brightness, new_color) + i++ + if(i % 30 == 0) + sleep(1) diff --git a/code/modules/planet/sif.dm b/code/modules/planet/sif.dm index 7b95d387bf..303d05f509 100644 --- a/code/modules/planet/sif.dm +++ b/code/modules/planet/sif.dm @@ -13,6 +13,7 @@ var/datum/planet/sif/planet_sif = null /datum/planet/sif/New() ..() + planet_sif = src weather_holder = new /datum/weather_holder/sif(src) // Cold weather is also nice. // This code is horrible. @@ -95,12 +96,11 @@ var/datum/planet/sif/planet_sif = null spawn(1) update_sun_deferred(2, new_brightness, new_color) -/datum/planet/proc/update_sun_deferred(var/new_range, var/new_brightness, var/new_color) - set background = 1 - set waitfor = 0 - var/i = 0 - for(var/turf/simulated/floor/T in outdoor_turfs) - T.set_light(new_range, new_brightness, new_color) - i++ - if(i % 30 == 0) - sleep(1) +// We're gonna pretend there are 32 hours in a Sif day instead of 32.64 for the purposes of not losing sanity. We lose 38m 24s but the alternative is a path to madness. +/datum/time/sif + seconds_in_day = 60 * 60 * 32 * 10 // 115,200 seconds. If we did 32.64 hours/day it would be around 117,504 seconds instead. + +// Returns the time datum of Sif. +/proc/get_sif_time() + if(planet_sif) + return planet_sif.current_time diff --git a/code/modules/planet/time.dm b/code/modules/planet/time.dm index 936ae0eadd..0b3008b668 100644 --- a/code/modules/planet/time.dm +++ b/code/modules/planet/time.dm @@ -70,7 +70,3 @@ answer = replacetext(answer, "mm", minute_text) answer = replacetext(answer, "ss", second_text) return answer - -// We're gonna pretend there are 32 hours in a Sif day instead of 32.64 for the purposes of not losing sanity. We lose 38m 24s but the alternative is a path to madness. -/datum/time/sif - seconds_in_day = 60 * 60 * 32 * 10 // 115,200 seconds. If we did 32.64 hours/day it would be around 117,504 seconds instead. \ No newline at end of file From 29eb4f5425ac032f31749440a5f91c31532d01fa Mon Sep 17 00:00:00 2001 From: Anewbe Date: Fri, 14 Apr 2017 23:25:46 -0500 Subject: [PATCH 12/18] Makes two more markings Taj only --- code/modules/mob/new_player/sprite_accessories.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 0c554f19cc..f6679a6dfe 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -1090,11 +1090,13 @@ name = "Color Patches" icon_state = "patches" body_parts = list(BP_L_FOOT,BP_R_FOOT,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_L_HAND,BP_R_HAND,BP_TORSO,BP_GROIN) + species_allowed = list("Tajara") patchesface name = "Color Patches (Face)" icon_state = "patchesface" body_parts = list(BP_HEAD) + species_allowed = list("Tajara") bands name = "Color Bands" From a29b8079530a85d9bc7ae8fb2103a963784ed273 Mon Sep 17 00:00:00 2001 From: Leshana Date: Sat, 15 Apr 2017 10:36:04 -0500 Subject: [PATCH 13/18] Allow set_opacity() to work for turfs too. Fixes lighting opacity not updating on mined rock walls and false doors. --- code/modules/lighting/lighting_atom.dm | 2 +- code/modules/mining/mine_turfs.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 6ec1e6ecd7..8700a65c6e 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -85,7 +85,7 @@ return opacity = new_opacity - var/turf/T = loc + var/turf/T = isturf(src) ? src : loc if(!isturf(T)) return diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index cc7c8c40bb..32ca92b311 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -82,7 +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) - // TODO - Look into this + recalc_atom_opacity() reconsider_lights() if(air_master) air_master.mark_for_update(src) From f4444f2dad01002e456cf9e9f2a5609ff6cb5275 Mon Sep 17 00:00:00 2001 From: Anewbe Date: Sat, 15 Apr 2017 20:35:17 -0500 Subject: [PATCH 14/18] Sorta fixes the syntax on the *halt emote --- code/modules/mob/living/silicon/robot/emote.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index a75a8f4bcc..84825559b0 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -230,7 +230,7 @@ if("halt") if (istype(module,/obj/item/weapon/robot_module/robot/security)) - message = "[src]'s speakers skreech, \"Halt! Security!\"." + message = "'s speakers skreech, \"Halt! Security!\"." playsound(src.loc, 'sound/voice/halt.ogg', 50, 0) m_type = 2 From e934e52f054b34523febd942f65ecab2601644ff Mon Sep 17 00:00:00 2001 From: MagmaRam Date: Sun, 16 Apr 2017 12:57:15 -0500 Subject: [PATCH 15/18] Changelog update --- html/changelog.html | 19 ++++++++++ html/changelogs/.all_changelog.yml | 13 +++++++ .../changelogs/Anewbe - Bar Weapon Permit.yml | 36 ------------------ html/changelogs/Anewbe - Burn Bloodloss.yml | 36 ------------------ html/changelogs/Anewbe - Hardhats.yml | 37 ------------------ html/changelogs/LorenLuke - PDA Altclick.yml | 36 ------------------ html/changelogs/Yoshax-Scrubbers.yml | 38 ------------------- 7 files changed, 32 insertions(+), 183 deletions(-) delete mode 100644 html/changelogs/Anewbe - Bar Weapon Permit.yml delete mode 100644 html/changelogs/Anewbe - Burn Bloodloss.yml delete mode 100644 html/changelogs/Anewbe - Hardhats.yml delete mode 100644 html/changelogs/LorenLuke - PDA Altclick.yml delete mode 100644 html/changelogs/Yoshax-Scrubbers.yml diff --git a/html/changelog.html b/html/changelog.html index 43adc4d3b8..79d2848774 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,25 @@ -->

+

16 April 2017

+

Anewbe updated:

+
    +
  • Bartenders now spawn with their shotgun permit. Click on it in hand to name it.
  • +
  • Lessens the bloodloss from severe burn damage.
  • +
  • Hardhats now give some ear protection.
  • +
  • Hardhats can no longer fit in pockets.
  • +
+

LorenLuke updated:

+
    +
  • Allows removal of PDA ID with alt-click.
  • +
+

Yosh updated:

+
    +
  • Scrubbers now scrub Phoron by default.
  • +
  • Scrubbers now have the first dangerzone at anything more than 0 Phoron.
  • +
  • No longer will you attack Alarms with your ID when trying to unlock them.
  • +
+

12 April 2017

Anewbe updated:

    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 1965caf7b0..a708517abe 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -3410,3 +3410,16 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Sin4: - bugfix: You can no longer ascend a table by walking from a flipped table to a non-flipped one. +2017-04-16: + Anewbe: + - rscadd: Bartenders now spawn with their shotgun permit. Click on it in hand to + name it. + - experiment: Lessens the bloodloss from severe burn damage. + - rscadd: Hardhats now give some ear protection. + - rscdel: Hardhats can no longer fit in pockets. + LorenLuke: + - rscadd: Allows removal of PDA ID with alt-click. + Yosh: + - 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. diff --git a/html/changelogs/Anewbe - Bar Weapon Permit.yml b/html/changelogs/Anewbe - Bar Weapon Permit.yml deleted file mode 100644 index 96bbdd6895..0000000000 --- a/html/changelogs/Anewbe - Bar Weapon Permit.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# 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." diff --git a/html/changelogs/Anewbe - Burn Bloodloss.yml b/html/changelogs/Anewbe - Burn Bloodloss.yml deleted file mode 100644 index 512606ca80..0000000000 --- a/html/changelogs/Anewbe - Burn Bloodloss.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# 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: - - experiment: "Lessens the bloodloss from severe burn damage." \ No newline at end of file diff --git a/html/changelogs/Anewbe - Hardhats.yml b/html/changelogs/Anewbe - Hardhats.yml deleted file mode 100644 index d3ea8beea7..0000000000 --- a/html/changelogs/Anewbe - Hardhats.yml +++ /dev/null @@ -1,37 +0,0 @@ -################################ -# 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." diff --git a/html/changelogs/LorenLuke - PDA Altclick.yml b/html/changelogs/LorenLuke - PDA Altclick.yml deleted file mode 100644 index 8e8b142b6a..0000000000 --- a/html/changelogs/LorenLuke - PDA Altclick.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################ -# 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: LorenLuke - -# 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: "Allows removal of PDA ID with alt-click." diff --git a/html/changelogs/Yoshax-Scrubbers.yml b/html/changelogs/Yoshax-Scrubbers.yml deleted file mode 100644 index 350f0554bd..0000000000 --- a/html/changelogs/Yoshax-Scrubbers.yml +++ /dev/null @@ -1,38 +0,0 @@ -################################ -# 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." From b9dd5233a6902ba4543ce50fee8f49a0fe4703d7 Mon Sep 17 00:00:00 2001 From: Leshana Date: Sun, 16 Apr 2017 14:46:09 -0400 Subject: [PATCH 16/18] Fix mining turfs permenently unlit. * Mining turfs generated during random mapgen get their opacity changed without a call to recalc_atom_opacity() becuase the game ticker has not started. Make sure we call it even before that point. --- code/modules/mining/mine_turfs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index 32ca92b311..bf59f3c382 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -81,8 +81,8 @@ var/list/mining_overlay_cache = list() /turf/simulated/mineral/proc/update_general() update_icon(1) + recalc_atom_opacity() if(ticker && ticker.current_state == GAME_STATE_PLAYING) - recalc_atom_opacity() reconsider_lights() if(air_master) air_master.mark_for_update(src) From cec43bf3d22014bc4abc238c3cca757afeaefc18 Mon Sep 17 00:00:00 2001 From: MagmaRam Date: Mon, 17 Apr 2017 11:19:39 -0500 Subject: [PATCH 17/18] Updates in-game EVA manual --- code/game/objects/items/weapons/manuals.dm | 74 ++++++++++++++++------ html/changelogs/MagmaRam - evabook.yml | 36 +++++++++++ 2 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 html/changelogs/MagmaRam - evabook.yml diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index c89f41056f..6ea812ad3a 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -1113,10 +1113,10 @@ "} /obj/item/weapon/book/manual/evaguide - name = "EVA Gear and You: Not Spending All Day Inside" + name = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition" icon_state = "evabook" author = "Maria Crash, Senior Atmospherics Technician" - title = "EVA Gear and You: Not Spending All Day Inside" + title = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition" dat = {"