mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 18:53:06 +00:00
Conflicts: baystation12.dme code/ATMOSPHERICS/components/unary/vent_pump.dm code/ATMOSPHERICS/components/unary/vent_scrubber.dm code/__HELPERS/game.dm code/controllers/shuttle_controller.dm code/datums/datumvars.dm code/datums/supplypacks.dm code/defines/obj/storage.dm code/defines/obj/vending.dm code/defines/obj/weapon.dm code/defines/procs/command_alert.dm code/game/area/Space Station 13 areas.dm code/game/atoms.dm code/game/gamemodes/changeling/modularchangling.dm code/game/gamemodes/cult/runes.dm code/game/gamemodes/events.dm code/game/gamemodes/events/dust.dm code/game/gamemodes/events/miniblob.dm code/game/gamemodes/events/ninja_equipment.dm code/game/gamemodes/factions.dm code/game/gamemodes/game_mode.dm code/game/gamemodes/gameticker.dm code/game/jobs/access.dm code/game/jobs/job/assistant.dm code/game/jobs/job/captain.dm code/game/jobs/job/civilian.dm code/game/jobs/job/engineering.dm code/game/jobs/job/medical.dm code/game/jobs/job/science.dm code/game/jobs/job/security.dm code/game/jobs/job_controller.dm code/game/machinery/alarm.dm code/game/machinery/atmo_control.dm code/game/machinery/atmoalter/area_atmos_computer.dm code/game/machinery/bots/medbot.dm code/game/machinery/computer/HolodeckControl.dm code/game/machinery/computer/ai_core.dm code/game/machinery/computer/atmos_alert.dm code/game/machinery/computer/card.dm code/game/machinery/computer/cloning.dm code/game/machinery/computer/computer.dm code/game/machinery/computer/message.dm code/game/machinery/computer/pod.dm code/game/machinery/computer/prisoner.dm code/game/machinery/computer/syndicate_shuttle.dm code/game/machinery/constructable_frame.dm code/game/machinery/doors/door.dm code/game/machinery/doors/firedoor.dm code/game/machinery/newscaster.dm code/game/machinery/recharger.dm code/game/machinery/spaceheater.dm code/game/machinery/telecomms/broadcaster.dm code/game/machinery/telecomms/logbrowser.dm code/game/machinery/telecomms/machine_interactions.dm code/game/machinery/teleporter.dm code/game/machinery/wishgranter.dm code/game/mecha/mech_fabricator.dm code/game/mecha/working/ripley.dm code/game/objects/items.dm code/game/objects/items/blueprints.dm code/game/objects/items/devices/flashlight.dm code/game/objects/items/devices/radio/encryptionkey.dm code/game/objects/items/devices/uplinks.dm code/game/objects/items/stacks/medical.dm code/game/objects/items/weapons/cards_ids.dm code/game/objects/items/weapons/gift_wrappaper.dm code/game/objects/items/weapons/hydroponics.dm code/game/objects/items/weapons/manuals.dm code/game/objects/items/weapons/secstorage/secstorage.dm code/game/objects/items/weapons/storage/backpack.dm code/game/objects/items/weapons/storage/firstaid.dm code/game/objects/items/weapons/storage/kit.dm code/game/objects/items/weapons/storage/storage.dm code/game/objects/items/weapons/storage/toolbox.dm code/game/objects/items/weapons/storage/uplink_kits.dm code/game/objects/items/weapons/stunbaton.dm code/game/objects/items/weapons/surgery_tools.dm code/game/objects/items/weapons/tools.dm code/game/objects/structures/crates_lockers/closets/secure/cargo.dm code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm code/game/objects/structures/crates_lockers/closets/secure/scientist.dm code/game/objects/structures/crates_lockers/closets/secure/security.dm code/game/objects/structures/electricchair.dm code/game/objects/structures/extinguisher.dm code/game/objects/structures/watercloset.dm code/game/objects/weapons.dm code/game/turfs/turf.dm code/game/vehicles/airtight/airtight.dm code/game/vehicles/vehicle.dm code/global.dm code/modules/DetectiveWork/evidence.dm code/modules/admin/IsBanned.dm code/modules/admin/player_panel.dm code/modules/admin/verbs/diagnostics.dm code/modules/admin/verbs/getlogs.dm code/modules/client/preferences.dm code/modules/clothing/masks/miscellaneous.dm code/modules/clothing/spacesuits/rig.dm code/modules/clothing/suits/miscellaneous.dm code/modules/critters/critter_defenses.dm code/modules/detectivework/scanner.dm code/modules/flufftext/Hallucination.dm code/modules/food/recipes_microwave.dm code/modules/mining/mine_items.dm code/modules/mining/mine_turfs.dm code/modules/mining/satchel_ore_boxdm.dm code/modules/mob/dead/observer/observer.dm code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm code/modules/mob/living/carbon/alien/special/facehugger.dm code/modules/mob/living/carbon/carbon.dm code/modules/mob/living/carbon/carbon_defines.dm code/modules/mob/living/carbon/human/human.dm code/modules/mob/living/carbon/human/human_attackhand.dm code/modules/mob/living/carbon/human/life.dm code/modules/mob/living/carbon/human/say.dm code/modules/mob/living/carbon/human/update_icons.dm code/modules/mob/living/carbon/metroid/metroid.dm code/modules/mob/living/living.dm code/modules/mob/living/silicon/ai/life.dm code/modules/mob/living/silicon/robot/robot.dm code/modules/mob/mob_cleanup.dm code/modules/mob/mob_defines.dm code/modules/mob/mob_transformation_simple.dm code/modules/paperwork/clipboard.dm code/modules/paperwork/folders.dm code/modules/paperwork/paper.dm code/modules/paperwork/paperbin.dm code/modules/paperwork/pen.dm code/modules/power/cable.dm code/modules/projectiles/guns/energy/special.dm code/modules/projectiles/guns/projectile/revolver.dm code/modules/projectiles/projectile/change.dm code/modules/projectiles/projectile/special.dm code/modules/reagents/Chemistry-Reagents.dm code/modules/reagents/reagent_containers/glass.dm code/modules/reagents/reagent_containers/hypospray.dm code/modules/recycling/sortingmachinery.dm code/modules/research/designs.dm config/config.txt html/changelog.html icons/mob/head.dmi icons/mob/hud.dmi icons/mob/items_lefthand.dmi icons/mob/items_righthand.dmi icons/mob/mask.dmi icons/mob/suit.dmi icons/mob/ties.dmi icons/mob/uniform.dmi icons/obj/clothing/hats.dmi icons/obj/clothing/masks.dmi icons/obj/clothing/suits.dmi icons/obj/clothing/ties.dmi icons/obj/grenade.dmi icons/obj/projectiles.dmi Signed-off-by: Cael_Aislinn <cael_aislinn@yahoo.com.au>
374 lines
13 KiB
Plaintext
374 lines
13 KiB
Plaintext
/*
|
|
Modified DynamicAreaLighting for TGstation - Coded by Carnwennan
|
|
|
|
This is TG's 'new' lighting system. It's basically a heavily modified combination of Forum_Account's and
|
|
ShadowDarke's respective lighting libraries. Credits, where due, to them.
|
|
|
|
Like sd_DAL (what we used to use), it changes the shading overlays of areas by splitting each type of area into sub-areas
|
|
by using the var/tag variable and moving turfs into the contents list of the correct sub-area.
|
|
|
|
Unlike sd_DAL however it uses a queueing system. Everytime we call a change to opacity or luminosity
|
|
(through SetOpacity() or SetLuminosity()) we are simply updating variables and scheduling certain lights/turfs for an
|
|
update. Actual updates are handled periodically by the lighting_controller. This carries additional overheads, however it
|
|
means that each thing is changed only once per lighting_controller.processing_interval ticks. Allowing for greater control
|
|
over how much priority we'd like lighting updates to have. It also makes it possible for us to simply delay updates by
|
|
setting lighting_controller.processing = 0 at say, the start of a large explosion, waiting for it to finish, and then
|
|
turning it back on with lighting_controller.processing = 1.
|
|
|
|
Unlike our old system there is a hardcoded maximum luminosity. This is to discourage coders using large luminosity values
|
|
for dynamic lighting, as the cost of lighting grows rapidly at large luminosity levels (especially when changing opacity
|
|
at runtime)
|
|
|
|
Also, in order for the queueing system to work, each light remembers the effect it casts on each turf. This is going to
|
|
have larger memory requirements than our previous system but hopefully it's worth the hassle for the greater control we
|
|
gain. Besides, there are far far worse uses of needless lists in the game, it'd be worth pruning some of them to offset
|
|
costs.
|
|
|
|
Known Issues/TODO:
|
|
admin-spawned turfs will have broken lumcounts. Not willing to fix it at this moment
|
|
mob luminosity will be lower than expected when one of multiple light sources is dropped after exceeding the maximum luminosity
|
|
Shuttles still do not have support for dynamic lighting (I hope to fix this at some point)
|
|
No directional lighting support. Fairly easy to add this and the code is ready.
|
|
*/
|
|
|
|
#define LIGHTING_MAX_LUMINOSITY 12 //Hard maximum luminosity to prevet lag which could be caused by coders making mini-suns
|
|
#define LIGHTING_MAX_LUMINOSITY_MOB 7 //Mobs get their own max because 60-odd human suns running around would be pretty silly
|
|
#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays
|
|
#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha7.dmi' //Icon used for lighting shading effects
|
|
|
|
datum/light_source
|
|
var/atom/owner
|
|
var/changed = 1
|
|
var/mobile = 1
|
|
var/list/effect = list()
|
|
|
|
var/__x = 0 //x coordinate at last update
|
|
var/__y = 0 //y coordinate at last update
|
|
|
|
|
|
New(atom/A)
|
|
if(!istype(A))
|
|
CRASH("The first argument to the light object's constructor must be the atom that is the light source. Expected atom, received '[A]' instead.")
|
|
|
|
..()
|
|
owner = A
|
|
|
|
if(istype(owner, /atom/movable)) mobile = 1 //apparantly this is faster than type-checking
|
|
else mobile = 0 //Perhaps removing support for luminous turfs would be a good idea.
|
|
|
|
__x = owner.x
|
|
__y = owner.y
|
|
|
|
// the lighting object maintains a list of all light sources
|
|
lighting_controller.lights += src
|
|
|
|
|
|
//Check a light to see if its effect needs reprocessing. If it does, remove any old effect and create a new one
|
|
proc/check()
|
|
if(!owner)
|
|
remove_effect()
|
|
return 1 //causes it to be removed from our list of lights. The garbage collector will then destroy it.
|
|
|
|
if(mobile)
|
|
// check to see if we've moved since last update
|
|
if(owner.x != __x || owner.y != __y)
|
|
__x = owner.x
|
|
__y = owner.y
|
|
changed = 1
|
|
|
|
if(changed)
|
|
changed = 0
|
|
remove_effect()
|
|
return add_effect()
|
|
return 0
|
|
|
|
|
|
proc/remove_effect()
|
|
// before we apply the effect we remove the light's current effect.
|
|
if(effect.len)
|
|
for(var/turf in effect) // negate the effect of this light source
|
|
var/turf/T = turf
|
|
T.update_lumcount(-effect[T])
|
|
effect.Cut() // clear the effect list
|
|
|
|
proc/add_effect()
|
|
// only do this if the light is turned on and is on the map
|
|
if(owner.loc && owner.luminosity > 0)
|
|
effect = new_effect() // identify the effects of this light source
|
|
for(var/turf in effect)
|
|
var/turf/T = turf
|
|
T.update_lumcount(effect[T]) // apply the effect
|
|
return 0
|
|
else
|
|
owner.light = null
|
|
return 1 //cause the light to be removed from the lights list and garbage collected once it's no
|
|
//longer referenced by the queue
|
|
|
|
proc/new_effect()
|
|
. = list()
|
|
|
|
for(var/turf/T in view(owner.luminosity, owner))
|
|
// var/area/A = T.loc
|
|
// if(!A) continue
|
|
var/change_in_lumcount = lum(T)
|
|
if(change_in_lumcount > 0)
|
|
.[T] = change_in_lumcount
|
|
|
|
return .
|
|
|
|
|
|
proc/lum(turf/A)
|
|
return owner.luminosity - max(abs(A.x-__x),abs(A.y-__y))
|
|
// var/dist = cheap_hypotenuse(A.x,A.y,__x,__y) //fetches the pythagorean distance between A and the light
|
|
// if(owner.luminosity < dist) //if the turf is outside the radius the light doesn't illuminate it
|
|
// return 0
|
|
// return round(owner.luminosity - (dist/2),0.1)
|
|
|
|
atom
|
|
var/datum/light_source/light
|
|
|
|
//Turfs with opacity when they are constructed will trigger nearby lights to update
|
|
//Turfs atoms with luminosity when they are constructed will create a light_source automatically
|
|
//TODO: lag reduction
|
|
turf/New()
|
|
..()
|
|
if(opacity)
|
|
UpdateAffectingLights()
|
|
if(luminosity)
|
|
world.log << "[type] has luminosity at New()"
|
|
if(light) world.log << "## WARNING: [type] - Don't set lights up manually during New(), We do it automatically."
|
|
light = new(src)
|
|
|
|
//Movable atoms with opacity when they are constructed will trigger nearby lights to update
|
|
//Movable atoms with luminosity when they are constructed will create a light_source automatically
|
|
//TODO: lag reduction
|
|
atom/movable/New()
|
|
..()
|
|
if(opacity)
|
|
UpdateAffectingLights()
|
|
if(luminosity)
|
|
if(light) world.log << "## WARNING: [type] - Don't set lights up manually during New(), We do it automatically."
|
|
light = new(src)
|
|
|
|
//Turfs with opacity will trigger nearby lights to update at next lighting process.
|
|
//TODO: is this really necessary? Removing it could help reduce lag during singulo-mayhem somewhat
|
|
turf/Del()
|
|
if(opacity)
|
|
UpdateAffectingLights()
|
|
..()
|
|
|
|
//Objects with opacity will trigger nearby lights to update at next lighting process.
|
|
atom/movable/Del()
|
|
if(opacity)
|
|
UpdateAffectingLights()
|
|
..()
|
|
|
|
//Sets our luminosity. Enforces a hardcoded maximum luminosity by default. This maximum can be overridden but it is extremely
|
|
//unwise to do so.
|
|
//If we have no light it will create one.
|
|
//If we are setting luminosity to 0 the light will be cleaned up and delted once all its queues are complete
|
|
//if we have a light already it is merely updated
|
|
atom/proc/SetLuminosity(new_luminosity, max_luminosity = LIGHTING_MAX_LUMINOSITY)
|
|
if(new_luminosity < 0)
|
|
new_luminosity = 0
|
|
// world.log << "## WARNING: [type] - luminosity cannot be negative"
|
|
else if(max_luminosity < new_luminosity)
|
|
new_luminosity = max_luminosity
|
|
// if(luminosity != new_luminosity)
|
|
// world.log << "## WARNING: [type] - LIGHT_MAX_LUMINOSITY exceeded"
|
|
|
|
if(isturf(loc))
|
|
if(light)
|
|
if(luminosity != new_luminosity) //TODO: remove lights from the light list when they're not luminous? DONE in add_effect
|
|
light.changed = 1
|
|
else
|
|
if(new_luminosity)
|
|
light = new(src)
|
|
|
|
luminosity = new_luminosity
|
|
|
|
//Snowflake code to prevent mobs becoming suns (lag-prevention)
|
|
mob/SetLuminosity(new_luminosity)
|
|
..(new_luminosity,LIGHTING_MAX_LUMINOSITY_MOB)
|
|
|
|
//change our opacity (defaults to toggle), and then update all lights that affect us.
|
|
atom/proc/SetOpacity(var/new_opacity)
|
|
if(new_opacity == null) new_opacity = !opacity
|
|
else if(opacity == new_opacity) return
|
|
opacity = new_opacity
|
|
|
|
UpdateAffectingLights()
|
|
|
|
//set the changed status of all lights which could have possibly lit this atom.
|
|
//We don't need to worry about lights which lit us but moved away, since they will have change status set already
|
|
atom/proc/UpdateAffectingLights()
|
|
var/turf/T = src
|
|
if(!isturf(T))
|
|
T = loc
|
|
if(!isturf(T)) return
|
|
for(var/atom in range(LIGHTING_MAX_LUMINOSITY,T)) //TODO: this will probably not work very well :(
|
|
var/atom/A = atom
|
|
if(A.light && A.luminosity)
|
|
A.light.changed = 1 //force it to update at next process()
|
|
|
|
// for(var/light in lighting_controller.lights) //TODO: this will probably laaaaaag
|
|
// var/datum/light_source/L = light
|
|
// if(L.changed) continue
|
|
// if(!L.owner) continue
|
|
// if(!L.owner.luminosity) continue
|
|
// if(src in L.effect)
|
|
// L.changed = 1
|
|
|
|
turf
|
|
var/lighting_lumcount = 0
|
|
var/lighting_changed = 0
|
|
|
|
turf/space
|
|
lighting_lumcount = 4 //starlight
|
|
|
|
turf/proc/update_lumcount(amount)
|
|
lighting_lumcount += amount
|
|
// if(lighting_lumcount < 0 || lighting_lumcount > 100)
|
|
// world.log << "## WARNING: [type] ([src]) lighting_lumcount = [lighting_lumcount]"
|
|
if(!lighting_changed)
|
|
lighting_controller.changed_turfs += src
|
|
lighting_changed = 1
|
|
|
|
turf/proc/shift_to_subarea()
|
|
lighting_changed = 0
|
|
var/area/Area = loc
|
|
|
|
if(!istype(Area) || !Area.lighting_use_dynamic) return
|
|
|
|
// change the turf's area depending on its brightness
|
|
// restrict light to valid levels
|
|
var/light = min(max(round(lighting_lumcount,1),0),lighting_controller.lighting_states)
|
|
|
|
var/find = findtextEx(Area.tag, "sd_L")
|
|
var/new_tag = copytext(Area.tag, 1, find)
|
|
new_tag += "sd_L[light]"
|
|
|
|
if(Area.tag!=new_tag) //skip if already in this area
|
|
|
|
var/area/A = locate(new_tag) // find an appropriate area
|
|
|
|
if(!A)
|
|
|
|
A = new Area.type() // create area if it wasn't found
|
|
// replicate vars
|
|
for(var/V in Area.vars)
|
|
switch(V)
|
|
if("contents","lighting_overlay","overlays") continue
|
|
else
|
|
if(issaved(Area.vars[V])) A.vars[V] = Area.vars[V]
|
|
|
|
A.tag = new_tag
|
|
A.lighting_subarea = 1
|
|
A.SetLightLevel(light)
|
|
|
|
Area.related += A
|
|
|
|
A.contents += src // move the turf into the area
|
|
|
|
area
|
|
var/lighting_use_dynamic = 1 //Turn this flag off to prevent sd_DynamicAreaLighting from affecting this area
|
|
var/image/lighting_overlay //tracks the darkness image of the area for easy removal
|
|
var/lighting_subarea = 0 //tracks whether we're a lighting sub-area
|
|
|
|
proc/SetLightLevel(light)
|
|
if(!src) return
|
|
if(light <= 0)
|
|
light = 0
|
|
luminosity = 0
|
|
else
|
|
if(light > lighting_controller.lighting_states)
|
|
light = lighting_controller.lighting_states
|
|
luminosity = 1
|
|
|
|
if(lighting_overlay)
|
|
overlays -= lighting_overlay
|
|
lighting_overlay.icon_state = "[light]"
|
|
else
|
|
lighting_overlay = image(LIGHTING_ICON,,num2text(light),LIGHTING_LAYER)
|
|
|
|
overlays += lighting_overlay
|
|
|
|
proc/InitializeLighting() //TODO: could probably improve this bit ~Carn
|
|
if(!tag) tag = "[type]"
|
|
if(!lighting_use_dynamic)
|
|
if(!lighting_subarea) // see if this is a lighting subarea already
|
|
//show the dark overlay so areas, not yet in a lighting subarea, won't be bright as day and look silly.
|
|
SetLightLevel(4)
|
|
|
|
atom
|
|
var/light_on = 0 //Am I emitting light?
|
|
var/brightness_on = 0 //Luminosity when the above: light_on = 1
|
|
|
|
//Called when turning off or dropping a flashlight for ex.
|
|
//It checks the users slots for another source of light, and return the appropriate brightness. 0 if no other source is found
|
|
proc/search_light(mob/M, obj/item/W as obj)
|
|
var/list/slots
|
|
var/obj/item/I
|
|
var brightness = 0 //the new brightness to be returned
|
|
|
|
if (istype(M, /mob/living/carbon/human))
|
|
var/mob/living/carbon/human/H = M
|
|
slots = list (
|
|
"l_hand",
|
|
"r_hand",
|
|
"belt",
|
|
"head",
|
|
"l_pocket",
|
|
"r_pocket",
|
|
"s_store")
|
|
|
|
for (var/slot in slots)
|
|
switch(slot)
|
|
if("belt")
|
|
I = H.belt
|
|
if("head")
|
|
I = H.head
|
|
if("l_hand")
|
|
I = H.l_hand
|
|
if("r_hand")
|
|
I = H.r_hand
|
|
if("l_pocket")
|
|
I = H.l_store
|
|
if("r_pocket")
|
|
I = H.r_store
|
|
if("s_store")
|
|
I = H.s_store
|
|
if (I)
|
|
if ((I.light_on) && (I != W)) //an item emitting light other than itself
|
|
if (I.brightness_on > brightness)
|
|
brightness = I.brightness_on
|
|
|
|
else if (istype(M, /mob/living/carbon/monkey))
|
|
slots = list (
|
|
"l_hand",
|
|
"r_hand")
|
|
|
|
for (var/slot in slots)
|
|
switch(slot)
|
|
if("l_hand")
|
|
I = M.l_hand
|
|
if("r_hand")
|
|
I = M.r_hand
|
|
if (I)
|
|
if ((I.light_on) && (I != W)) //an item emitting light other than itself
|
|
if (I.brightness_on > brightness)
|
|
brightness = I.brightness_on
|
|
|
|
else
|
|
for (I in M.contents) //Justin Case
|
|
if (I)
|
|
if ((I.light_on) && (I != W)) //an item emitting light other than itself
|
|
if (I.brightness_on > brightness)
|
|
brightness = I.brightness_on
|
|
|
|
return brightness
|
|
|
|
#undef LIGHTING_MAX_LUMINOSITY
|
|
#undef LIGHTING_MAX_LUMINOSITY_MOB
|
|
#undef LIGHTING_LAYER
|
|
//#undef LIGHTING_ICON |