Files
Bubberstation/code/modules/unit_tests/create_and_destroy.dm
T
GoldenAlpharex 702c3e2ce6 Total refactor of the way we handle and draw mutant_bodyparts (+ missed mirrors that motivated me to do this) (#19635)
* Splits mutant_bodyparts.dmi into several subfiles. (#71408)

Splits the `mutant_bodyparts.dmi` file up, as was suggested, but not
implemented, in #69302.

The following new files were created in the `icons\mob\species` folder,
containing the listed sprites:

- `lizard\lizard_misc.dmi`: Snouts, horns, frills, and body markings.
Anything I didn't feel needed an entire file to itself.
- `lizard\lizard_spines.dmi`: Lizard spines, both animated and not.
- `lizard\lizard_tails.dmi`: Lizard tails, both animated and not.
- `human\cat_features.dmi`: Felinid ears and tails.
- `monkey\monkey_tail.dmi`: The monkey tail.
- `mush_cap.dmi`: The lone mushperson cap, so easily forgotten.

Additionally, I moved `wings.dmi` from `mob\clothing` to `mob\species`.
I'm not sure what it was doing there.

`mutant_bodyparts.dmi` was something of a mess, with parts from four
different species thrown together haphazardly. It probably made sense in
earlier days when "mutant humans" were few and far between, but
splitting it up makes it more intuitive to find these sprites, and
brings it in line with other species - namely, moths. It also means that
any future sprites of these types added will not bloat an oversized file
even more.

Moving the wings file to the species folder just makes sense, because
wings are not a type of clothing.
🆑
refactor: Removed mutant_bodyparts.dmi and distributed its sprites in a
more sensible and granular manner.
/🆑

* External Organ Rework: new bodypart_overlay system (#72734)

Bodypart overlays are now drawn by the new /datum/bodypart_overlay
datum.

External organs no longer draw anything and instead add a special
/datum/bodypart_overlay/mutant to the bodypart, which draws everything

Makes it way easier to add custom overlays to limbs, since the whole
system is now modularized and external organs are just one
implementation of it

I haven't moved anything but external organs to this new system, I'll
move eyes, bodymarkings, hair, lipstick etc to this later

New pipeline is as follows:
- External organ added to limb
- External organ adds /datum/bodypart_overlay/mutant to limb to
bodypart_overlays
- Limb updates its icon, looks for all /datum/bodypart_overlay in
bodypart_overlays
- Very cool new overlay on your limb!

closes #71820

🆑
refactor: External organs have been near-completely refactored.
admin: Admin-spawned external organs will load with a random icon and
color
fix: fixes angel wings not working for non-humans (it was so fucking
broken)
fix: fixes external organs being invisible if they werent initialized
with a human
/🆑

External organs are cool but are pretty limited in some ways. Making
stuff like synthetic organs is kinda fucked. I tried and it was dogshit.
Now you can just give an icon state and icon and you're good (using
/datum/bodypart_accessory/simple)

Stuff like eyes, cat ears and hair seem like good choices for extorgans,
but don't quite work for it because their icons work a lot differently.
This solves for it completely since any organ (or object or whatever)
can add it's own icon to a bodypart.

Want to add an iron plate to someones head? Go ahead. Want a heart to
stick out of someones chest? No problem.

Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>

* Makes the whole thing compile, although it most likely doesn't work

* Whoopsie daisy

* Now it's starting to work

* ok monkey

* Fixes tails some more

* Moth wings should be wings not moth wings

* Podpeople hair, yeah.

* Okay no more handle_mutant_bodyparts()

* Removed an useless variable from is_hidden()

* Fixes some more can_draw_on_bodypart (I'm gonna have to fix it upstream too)

* Removes the second useless argument of is_hidden()

* No, cat tails are no longer allowed to be snowflake

* Removes some useless variables from humans

* Whoops I forgot to commit this one

* How did I miss this one?

* Okay, yeah, tails and some other stuff show up now. Pod people hair too. Just many of them don't. Good enough for today.

* Okay so after a lot of pain and suffering, many mutant_bodyparts are now functional

* Everything works, except for ears, some of the taur layering, and moth markings (which never worked it seems)

* Alright skintones seem to work rather well now(?)

* Okay, my bad, now they do

* Moth markings can't be selected and won't be added anymore (since they're broken and integrated into the regular markings system anyway)

* Spines are no longer just matching the color of the limb they're attached to

* Moves taur organ and bodypart_overlay out of the sprite_accessory file for taurs, and fixes the taur rendering

* Linters moment

* Alright, spines are working and overlaying properly now

They just need to have a FRONT overlay for the /tg/ ones if we want them to display above the lizard tails, we already have that for vox spines thankfully.

* Disables moth_markings for good

* Fixes some rendering bugs

* Makes ears work (almost, rest of the code has MODsuit stuff in it too :( )

* Made the MODsuit overlays work :)

* Fixed horns and spines

* Fixes all of the screenshot tests

* Removes the Body Markings option from Appearances, use Augments+ instead (because that one works and is just better)

* Bye bye handle_mutant_bodyparts(), I won't miss you :)

* Forgot to take this out, whoops

* Fixes the moth antennae being unticked

* Removes some commented code in a modular file

* Fixes part of the create_and_destroy unit test

* Fixes a pretty big issue that caused some sprite_accessories to be removed from the global list because of the alter_form action (deep copies ftw)

* Fixes the runtimes related to pod_hair

* Gives the vox a better look in the prefs menu

* Fixes the appearance of functional wings

* Makes synths able to access their robotic wings

* Podpeople don't cause runtimes during CI anymore

* Fixes the random appearance of locked functional wings

* Fixed cat and lizard tails using the /tg/ sprites (we have slightly modified ones)

* Fixes the coloring on multi-colored sprites

* Fixes the code for the screenshot of the mammal screenshots and preview, so it has a tail

* Fixes the screenshot tests for a few species

* Fixes tails going invisible when wagging

* Fixes a runtime with horns

* Fixes showing/hiding mutant bodyparts not working at all

* (Hopefully) fully fixes all the issues related to extra and extra2-related icon_states.

* Fixes synth stuff and starts working on pod hair again

* I DID IT PODPEOPLE HAVE HAIR AGAIN

* Fixes the podpeople hair showing up on more than just podpeople

* Fixes the IPC screens showing up on species outside of synthetics

* Fixes a runtime in the pod_hair and the synth_screen's apply_to_human

* Updates the screenshots for podpeople and synths

* Fixes an issue with a certain type of organs

* Fixes another runtime, whoops

* Fixes the markings from /tg/'s Body Markings not being available in our markings system

* Re-adds support for taur-variants of certain mutant bodyparts

* Hopefully fixes a few more runtimes

* Adds some debug variable in the code for now, likely to be removed later if really necessary. This will make my life easier.

* Whoops, that needed to be backwards. :clown:

* Fixes the top snouts not rendering at all

* Fixes the issues where bodytypes weren't handled properly, and thus snouts were getting squished

* Fixes the weird placement of the Xeno Head Emissives in the prefs menu

* Removes a no-longer-necessary override of post_set_preference() in the limbs_and_markings middleware

* Reverts an unnecessary SKYRAT EDIT inside of commented out code

* No longer creates a new body every time we change prefs in the prefs menu, we reusing now lads

I know there's commented out code but I'm going to remove that in another commit

* Removed some more dead code.

* Fixes jellyfish ears being all white

* Fixes plasmaman limbs not displaying because I accidentally hardcoded the limb_id for all augments to "robotic"

---------

Co-authored-by: lizardqueenlexi <105025397+lizardqueenlexi@users.noreply.github.com>
Co-authored-by: Time-Green <timkoster1@hotmail.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
2023-03-16 13:07:24 +00:00

219 lines
10 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)
//Can't pass in a thing to glow
ignore += typesof(/obj/effect/abstract/eye_lighting)
//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)
//SKYRAT EDIT ADDITION - OUR IGNORES DOWN HERE
//Not designed to be spawned without a turf.
ignore += typesof(/obj/effect/abstract/liquid_turf)
//Not designed to be spawned individually.
ignore += typesof(/obj/structure/biohazard_blob)
//Unused - not supposed to be spawned without SSliquids
ignore += typesof(/turf/open/openspace/ocean)
//Baseturf editors can only go up to ten, stop this.
ignore += typesof(/obj/effect/baseturf_helper)
// It's the abstract base type, it shouldn't be spawned.
ignore += /obj/item/organ/external/genital
// These two are locked to one type only, and shouldn't be widely available, hence why they runtime otherwise.
// Can't be bothered adding more to them.
ignore += list(/obj/item/organ/external/neck_accessory, /obj/item/organ/external/head_accessory)
//SKYRAT EDIT END
//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
//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
var/time_needed = SSgarbage.collection_timeout[GC_QUEUE_CHECK]
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_HARDDELETE] = 10 SECONDS