mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-02-06 14:29:17 +00:00
## About The Pull Request The high luminosity eyes item was extremely out of date, broken, and full of copy paste code from atom lighting. Which is a shame because they were cool. On top of all that it was using a special light effect that was not very performant. I got rid of all that, hooked it into atom lighting as a new light type, and gave it a new TGUI as well because the old ui prompts were not great either. You can now pick a color at random if you want. You can also set the color and range before surgically implanting them. No more being forced to go through the color picker when you just want to change the range. Functionally they should pretty much should be the same as before with some bonus features (see below).  Closes https://github.com/tgstation/tgstation/issues/61041 Closes https://github.com/Skyrat-SS13/Skyrat-tg/issues/14685 This is 100% completed. I just finished fixing the slight translation bug when going from 0->1 range (see above gif) and that was the last thing on my bucket list. I happy enough with this at this point in time. --- EDIT: I have decided to add in one last new feature, and that is... independent settings for eye color. <details> <summary>You can now set eye color independently if you wish</summary>  </details> The eye color does not modify the light color in any way when set in this manner, but it can be used for cosmetic purposes. Kind of makes the item more like cybereyes from cyberpunk, which I think are pretty neat! </details> ### What I've done, in more detail: - refactored high luminosity eyes so they use the atom lighting system instead of the way they were doing it before - the new light type, `MOVABLE_LIGHT_BEAM` behaves similarly to directional lights, with some slight differences. it reuses the same lighting overlay sprites but scales them vertically to produce a more focused effect. The result can be seen above. This is in contrast to the old way, which spawned `range` number of individual 32x32 overlays and moved them around. This way should perform better as well as be more maintainable. - added a new TGUI interface for high luminosity eyes with buttons for range, a text field for a color hex, a color picker and randomizer - made the eye overlay emissive when the light is turned on - range goes from 0 to 5. at range 0, the light overlay is turned off and you are left with just the emissive eyes. - added a cosmetic functionality to this item that lets you change the color of your eyes independently of the lighting (and each other) - fixed a bug with directional flashlights sometimes not updating their lighting overlay if you pick them up without changing your direction --- ### Other Misc Fixes Being able to dynamically set range back and forth exposed some logic issues that had existed with directional light overlays and I have fixed those. That is why there are some edits in that file that may not appear readily obvious why they are there. Apart from that, two other bugs that come to mind: 1) eye code was supposed to keep track of the eye color you had before you got new eyes, but it was overwriting that every time you ran refresh(). 2) lighting was supposed to be turning off the light when range is set to 0, but it was not doing that properly. And of course besides that, there may have been a few instances of finding typos/tidying up code ## Why It's Good For The Game The code for this was like 6 years old and in desperate need of updating. Now it works, and has a nicer UI. ## Changelog 🆑 fix: high luminosity eyes light overlays now follow the user correctly qol: high luminosity eyes now have a tgui menu so you no longer have to go through the color picker every time you want to change the range. they also have a new setting that lets you change the color of your eyes independently of the light color. You can now have cybernetic heterochromia if you want fix: directional flashlights when picked up will now always update their cast light direction correctly no matter what dir you are facing refactor: refactors high luminosity eye code to better make use of the atom lighting system, adding a new light type 'MOVABLE_LIGHT_BEAM' /🆑
205 lines
9.4 KiB
Plaintext
205 lines
9.4 KiB
Plaintext
///Delete one of every type, sleep a while, then check to see if anything has gone fucky
|
|
/datum/unit_test/create_and_destroy
|
|
//You absolutely must run last
|
|
priority = TEST_CREATE_AND_DESTROY
|
|
|
|
GLOBAL_VAR_INIT(running_create_and_destroy, FALSE)
|
|
/datum/unit_test/create_and_destroy/Run()
|
|
//We'll spawn everything here
|
|
var/turf/spawn_at = run_loc_floor_bottom_left
|
|
var/list/ignore = list(
|
|
//Never meant to be created, errors out the ass for mobcode reasons
|
|
/mob/living/carbon,
|
|
//Nother template type, doesn't like being created with no seed
|
|
/obj/item/food/grown,
|
|
//And another
|
|
/obj/item/slimecross/recurring,
|
|
//This should be obvious
|
|
/obj/machinery/doomsday_device,
|
|
//Yet more templates
|
|
/obj/machinery/restaurant_portal,
|
|
//Template type
|
|
/obj/effect/mob_spawn,
|
|
//Template type
|
|
/obj/structure/holosign/robot_seat,
|
|
//Singleton
|
|
/mob/dview,
|
|
//Template type
|
|
/obj/item/bodypart,
|
|
//This is meant to fail extremely loud every single time it occurs in any environment in any context, and it falsely alarms when this unit test iterates it. Let's not spawn it in.
|
|
/obj/merge_conflict_marker,
|
|
//briefcase launchpads erroring
|
|
/obj/machinery/launchpad/briefcase,
|
|
//Both are abstract types meant to scream bloody murder if spawned in raw
|
|
/obj/item/organ/external,
|
|
/obj/item/organ/external/wings,
|
|
)
|
|
//Say it with me now, type template
|
|
ignore += typesof(/obj/effect/mapping_helpers)
|
|
//This turf existing is an error in and of itself
|
|
ignore += typesof(/turf/baseturf_skipover)
|
|
ignore += typesof(/turf/baseturf_bottom)
|
|
//This demands a borg, so we'll let if off easy
|
|
ignore += typesof(/obj/item/modular_computer/pda/silicon)
|
|
//This one demands a computer, ditto
|
|
ignore += typesof(/obj/item/modular_computer/processor)
|
|
//Very finiky, blacklisting to make things easier
|
|
ignore += typesof(/obj/item/poster/wanted)
|
|
//This expects a seed, we can't pass it
|
|
ignore += typesof(/obj/item/food/grown)
|
|
//Needs clients / mobs to observe it to exist. Also includes hallucinations.
|
|
ignore += typesof(/obj/effect/client_image_holder)
|
|
//Same to above. Needs a client / mob / hallucination to observe it to exist.
|
|
ignore += typesof(/obj/projectile/hallucination)
|
|
ignore += typesof(/obj/item/hallucinated)
|
|
//We don't have a pod
|
|
ignore += typesof(/obj/effect/pod_landingzone_effect)
|
|
ignore += typesof(/obj/effect/pod_landingzone)
|
|
//We have a baseturf limit of 10, adding more than 10 baseturf helpers will kill CI, so here's a future edge case to fix.
|
|
ignore += typesof(/obj/effect/baseturf_helper)
|
|
//No tauma to pass in
|
|
ignore += typesof(/mob/camera/imaginary_friend)
|
|
//No pod to gondola
|
|
ignore += typesof(/mob/living/simple_animal/pet/gondola/gondolapod)
|
|
//No heart to give
|
|
ignore += typesof(/obj/structure/ethereal_crystal)
|
|
//No linked console
|
|
ignore += typesof(/mob/camera/ai_eye/remote/base_construction)
|
|
//See above
|
|
ignore += typesof(/mob/camera/ai_eye/remote/shuttle_docker)
|
|
//Hangs a ref post invoke async, which we don't support. Could put a qdeleted check but it feels hacky
|
|
ignore += typesof(/obj/effect/anomaly/grav/high)
|
|
//See above
|
|
ignore += typesof(/obj/effect/timestop)
|
|
//Invoke async in init, skippppp
|
|
ignore += typesof(/mob/living/silicon/robot/model)
|
|
//This lad also sleeps
|
|
ignore += typesof(/obj/item/hilbertshotel)
|
|
//this boi spawns turf changing stuff, and it stacks and causes pain. Let's just not
|
|
ignore += typesof(/obj/effect/sliding_puzzle)
|
|
//Stacks baseturfs, can't be tested here
|
|
ignore += typesof(/obj/effect/temp_visual/lava_warning)
|
|
//Stacks baseturfs, can't be tested here
|
|
ignore += typesof(/obj/effect/landmark/ctf)
|
|
//Our system doesn't support it without warning spam from unregister calls on things that never registered
|
|
ignore += typesof(/obj/docking_port)
|
|
//Asks for a shuttle that may not exist, let's leave it alone
|
|
ignore += typesof(/obj/item/pinpointer/shuttle)
|
|
//This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK
|
|
ignore += typesof(/obj/structure/alien/resin/flower_bud)
|
|
//Needs a linked mecha
|
|
ignore += typesof(/obj/effect/skyfall_landingzone)
|
|
//Expects a mob to holderize, we have nothing to give
|
|
ignore += typesof(/obj/item/clothing/head/mob_holder)
|
|
//Needs cards passed into the initilazation args
|
|
ignore += typesof(/obj/item/toy/cards/cardhand)
|
|
//Needs a holodeck area linked to it which is not guarenteed to exist and technically is supposed to have a 1:1 relationship with computer anyway.
|
|
ignore += typesof(/obj/machinery/computer/holodeck)
|
|
//runtimes if not paired with a landmark
|
|
ignore += typesof(/obj/structure/industrial_lift)
|
|
// Runtimes if the associated machinery does not exist, but not the base type
|
|
ignore += subtypesof(/obj/machinery/airlock_controller)
|
|
// Always ought to have an associated escape menu. Any references it could possibly hold would need one regardless.
|
|
ignore += subtypesof(/atom/movable/screen/escape_menu)
|
|
|
|
var/list/cached_contents = spawn_at.contents.Copy()
|
|
var/original_turf_type = spawn_at.type
|
|
var/original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs
|
|
var/original_baseturf_count = length(original_baseturfs)
|
|
|
|
GLOB.running_create_and_destroy = TRUE
|
|
for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please
|
|
if(ispath(type_path, /turf))
|
|
spawn_at.ChangeTurf(type_path)
|
|
//We change it back to prevent baseturfs stacking and hitting the limit
|
|
spawn_at.ChangeTurf(original_turf_type, original_baseturfs)
|
|
if(original_baseturf_count != length(spawn_at.baseturfs))
|
|
TEST_FAIL("[type_path] changed the amount of baseturfs from [original_baseturf_count] to [length(spawn_at.baseturfs)]; [english_list(original_baseturfs)] to [islist(spawn_at.baseturfs) ? english_list(spawn_at.baseturfs) : spawn_at.baseturfs]")
|
|
//Warn if it changes again
|
|
original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs
|
|
original_baseturf_count = length(original_baseturfs)
|
|
else
|
|
var/atom/creation = new type_path(spawn_at)
|
|
if(QDELETED(creation))
|
|
continue
|
|
//Go all in
|
|
qdel(creation, force = TRUE)
|
|
//This will hold a ref to the last thing we process unless we set it to null
|
|
//Yes byond is fucking sinful
|
|
creation = null
|
|
|
|
//There's a lot of stuff that either spawns stuff in on create, or removes stuff on destroy. Let's cut it all out so things are easier to deal with
|
|
var/list/to_del = spawn_at.contents - cached_contents
|
|
if(length(to_del))
|
|
for(var/atom/to_kill in to_del)
|
|
qdel(to_kill)
|
|
|
|
GLOB.running_create_and_destroy = FALSE
|
|
//Hell code, we're bound to have ended the round somehow so let's stop if from ending while we work
|
|
SSticker.delay_end = TRUE
|
|
|
|
// Drastically lower the amount of time it takes to GC, since we don't have clients that can hold it up.
|
|
SSgarbage.collection_timeout[GC_QUEUE_CHECK] = 10 SECONDS
|
|
//Prevent the garbage subsystem from harddeling anything, if only to save time
|
|
SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = 10000 HOURS
|
|
//Clear it, just in case
|
|
cached_contents.Cut()
|
|
|
|
//Now that we've qdel'd everything, let's sleep until the gc has processed all the shit we care about
|
|
// + 2 seconds to ensure that everything gets in the queue.
|
|
var/time_needed = SSgarbage.collection_timeout[GC_QUEUE_CHECK] + 2 SECONDS
|
|
|
|
var/start_time = world.time
|
|
var/garbage_queue_processed = FALSE
|
|
|
|
sleep(time_needed)
|
|
while(!garbage_queue_processed)
|
|
var/list/queue_to_check = SSgarbage.queues[GC_QUEUE_CHECK]
|
|
//How the hell did you manage to empty this? Good job!
|
|
if(!length(queue_to_check))
|
|
garbage_queue_processed = TRUE
|
|
break
|
|
|
|
var/list/oldest_packet = queue_to_check[1]
|
|
//Pull out the time we deld at
|
|
var/qdeld_at = oldest_packet[1]
|
|
//If we've found a packet that got del'd later then we finished, then all our shit has been processed
|
|
if(qdeld_at > start_time)
|
|
garbage_queue_processed = TRUE
|
|
break
|
|
|
|
if(world.time > start_time + time_needed + 30 MINUTES) //If this gets us gitbanned I'm going to laugh so hard
|
|
TEST_FAIL("Something has gone horribly wrong, the garbage queue has been processing for well over 30 minutes. What the hell did you do")
|
|
break
|
|
|
|
//Immediately fire the gc right after
|
|
SSgarbage.next_fire = 1
|
|
//Unless you've seriously fucked up, queue processing shouldn't take "that" long. Let her run for a bit, see if anything's changed
|
|
sleep(20 SECONDS)
|
|
|
|
//Alright, time to see if anything messed up
|
|
var/list/cache_for_sonic_speed = SSgarbage.items
|
|
for(var/path in cache_for_sonic_speed)
|
|
var/datum/qdel_item/item = cache_for_sonic_speed[path]
|
|
if(item.failures)
|
|
TEST_FAIL("[item.name] hard deleted [item.failures] times out of a total del count of [item.qdels]")
|
|
if(item.no_respect_force)
|
|
TEST_FAIL("[item.name] failed to respect force deletion [item.no_respect_force] times out of a total del count of [item.qdels]")
|
|
if(item.no_hint)
|
|
TEST_FAIL("[item.name] failed to return a qdel hint [item.no_hint] times out of a total del count of [item.qdels]")
|
|
|
|
cache_for_sonic_speed = SSatoms.BadInitializeCalls
|
|
for(var/path in cache_for_sonic_speed)
|
|
var/fails = cache_for_sonic_speed[path]
|
|
if(fails & BAD_INIT_NO_HINT)
|
|
TEST_FAIL("[path] didn't return an Initialize hint")
|
|
if(fails & BAD_INIT_QDEL_BEFORE)
|
|
TEST_FAIL("[path] qdel'd in New()")
|
|
if(fails & BAD_INIT_SLEPT)
|
|
TEST_FAIL("[path] slept during Initialize()")
|
|
|
|
SSticker.delay_end = FALSE
|
|
//This shouldn't be needed, but let's be polite
|
|
SSgarbage.collection_timeout[GC_QUEUE_CHECK] = GC_CHECK_QUEUE
|
|
SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = GC_DEL_QUEUE
|