From 187554fccccc64b6ccb60a03c2ee10ab9683e60f Mon Sep 17 00:00:00 2001 From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun, 25 Aug 2024 17:47:18 +0100 Subject: [PATCH] Squashed commit of the following: commit 63c3663fa6c8add51bd52ca8b03e81e15d097b0b Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 17:40:53 2024 +0100 Update check_disposals.dm commit d1f29021f62292b7570405b1d9d8f6a00fbfb78a Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 17:15:12 2024 +0100 Fixes an issue with the disposals check commit b75d1a4abf202111128ca484e7d4c0ce20845124 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 16:45:56 2024 +0100 Entering a pipe backwards will send you the way that you came commit 7fb991fcd51e32dc161c8a3b11e31334091292a4 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 15:48:41 2024 +0100 Adds more information to the failure commit e3fe9459e8785bf57cab48e744c45eb5117268cb Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 16:42:09 2024 +0100 Bug fixes commit 92c3cc779edbdf784a0f39c63beecd165b1f0ed3 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 16:34:59 2024 +0100 Update pipe_sorting.dm commit a7528d40499d55abeed8785f2643a1538dae794b Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 16:28:39 2024 +0100 Fixes pipe sorting commit 1a6edebf08217adad15c9d70d422be890f04b2a9 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 15:32:38 2024 +0100 Fixes disposals detecting loops when there are none commit 21e0b93e7ba1946c3823f3be299efdf9777d6b89 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 15:21:36 2024 +0100 Fixes ending in a null location commit 3c8760026e2970d515fda830090e3073e786f0e3 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 15:14:56 2024 +0100 Update check_disposals.dm commit 9b0066201470d17c75758ad59bb7512a54ae9d83 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 14:58:11 2024 +0100 Adds the ability to build the unsorted pipe with the RPD commit d642d63dd5c7ea20f08f04a32c7299367264c298 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 14:57:34 2024 +0100 Only send unsorted things if they need sorting commit 126c24c600faaec5fc1488b685ce323275a05b05 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 14:50:07 2024 +0100 Adds an unsorted sorting pipe commit bb5052fa7effdab1ad0b56cda84a6de82c4f6af7 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 14:41:35 2024 +0100 Adds a somewhat hacky check to fix random maint room spawning commit d16c197d56b682021b5ab8764a64ce8222ff313f Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 14:22:30 2024 +0100 Random rooms will delete any directional things not placed on walls, fixes inconsistent indentation commit 072ddc82d72bfde19ac4f565fe354f94f1be3570 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:53:59 2024 +0100 Fixes some areas interacting wtih APC checks commit 274909c01636211fb6783497a9dce4da177e2b0d Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:44:00 2024 +0100 Update check_disposals.dm commit 9ca31dd34ab563822ae9aeaca1ab2d33f28ecd98 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:36:31 2024 +0100 Update check_disposals.dm commit 94c2085d6253e7e5db1d38ed03d1b1ba773b3e12 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:34:26 2024 +0100 Makes run ID properly unique commit 45360e1384fc9d62067d118d36d0ad0d5be01106 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:22:39 2024 +0100 Corrects a mistake commit a7b89ae93c25d8aa8f64bd28aa5186f893aabe97 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:17:17 2024 +0100 Fixes the stupidity of sort codes commit ff4d191b2a065522c8aea13b382afa334afeeafb Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:10:37 2024 +0100 Update check_disposals.dm commit 917e4014436d3a4882f6f79164999361ae34ac58 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 13:03:43 2024 +0100 Debug info commit ee5e62aaf347739d1ca466f6e8ce93dd83e8c9d6 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:57:54 2024 +0100 Fixes commit 90438aee31cd83b006d7efeca3e0184bbfcefbdc Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:50:25 2024 +0100 Update check_disposals.dm commit 34f9ff36682551695866cf0e9cf52fc9b216d219 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:42:14 2024 +0100 Account for the complete shitcode of disposals commit f84270f553dc1c5ac1a243411c95f3a01b0182f4 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:30:04 2024 +0100 Fixes disposal loop bug commit b42465ead5227d2ef996ea5ca7a745cbc2373254 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:24:32 2024 +0100 Fixes failures not being properly commit 360fdb9166094ec6913d41f722fda8521d9b23fc Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:18:41 2024 +0100 Correctly return when we encounter an error commit 5bb1bb3e8a6ae23b77392f8c782ac51d00f7156e Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:17:19 2024 +0100 Fixes infinite loop check commit 4fb080d2107c13c53b62c62360525aaed0a98cc5 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 12:02:48 2024 +0100 Another correction in disposal tests commit 602bb86fd93682eb7a10ab749c0a017abab884a5 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 11:57:56 2024 +0100 Fixes issue with disposal unit test check commit 4e95a427e55670f63a97188f4d9723c75d569f5c Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sun Aug 25 11:52:12 2024 +0100 Fixes crashing issues commit ff1888ec06795b390f4225de6720b20fd5e17777 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:32:50 2024 +0100 Re-enables atmos reset commit 366fb6c39446d46bd4dfe34fb279d371a5a82e03 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:29:38 2024 +0100 Disables restore_atmos commit d7db0d2b18dd6c84baf88edab3f7dc2e7cee3936 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:29:30 2024 +0100 Revert "Removes auxmos temporarilly" This reverts commit 4c42c82126a4ac5156cd0042b91ee6f33553755c. commit 4c42c82126a4ac5156cd0042b91ee6f33553755c Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:24:55 2024 +0100 Removes auxmos temporarilly commit 6bca77dd7b8f4419bc272aa76fd8cd0ca68599eb Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:18:43 2024 +0100 Corrects the DME target commit 60aee829f1c30523233b859b02e1ab2e3cae5e10 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:15:49 2024 +0100 Fixes compilation issues commit 4abb5d40c6237ed7dc297a13084de67c66ee489e Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:12:36 2024 +0100 Fixes compilation commit 9fc4a21c3448296eefe74edd5aac6ad4b10c7c1e Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:09:41 2024 +0100 Update run_server.sh Co-Authored-By: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-Authored-By: Jordan Dominion Co-Authored-By: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-Authored-By: Tastyfish <1185434+Tastyfish@users.noreply.github.com> commit 86236320e413a0738674fdaaf75aa3e9e12b9159 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 17:07:47 2024 +0100 Disables create and destroy commit 3ea806eecabc82caa919813d63e73f33cf6db9e3 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 16:58:40 2024 +0100 Adds some mapping testers commit ded19decb499da8eeb9cf663c195404301346227 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 16:00:20 2024 +0100 Adds mapping tests commit 305bbd23437afed2c11d9d61c90e37d9a211e4dc Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 15:50:03 2024 +0100 Updates check_grep to include the new commit cee5de3661811f4534465b67ffab9617d982e1bd Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 15:16:36 2024 +0100 Update run_server.sh commit 0c1d521a6983301eb6ce569f1c074d9efbb725b7 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 15:16:00 2024 +0100 I don't think we need mariaDB anymore commit 764a780950c75ca67e1cf8d74c4fef2baf521731 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:59:56 2024 +0100 Fixes qdel in optimisation commit 66c79d180b7c385d4eb2bd34fcdf649b24e9f5f4 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:52:52 2024 +0100 Create max_required_byond_client.txt Co-Authored-By: Kyle Spier-Swenson Co-Authored-By: Mothblocks <35135081+Mothblocks@users.noreply.github.com> commit 1d7c099e106d62b0484b7da5328823288a2a0a13 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:47:55 2024 +0100 Fixes references to TGstation isntead of Beestation commit 862579e89335da0b4d363ad9a2f6b772a4896f29 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:44:49 2024 +0100 Adds ticked file enforcement Co-Authored-By: san7890 Co-Authored-By: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> commit f560f45566c75b9c1e30f71b07b267bee9cd0f3e Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:42:38 2024 +0100 Adds trait validity commit c024799d28bb7618538be6fa4a9ea7d8a4991ca7 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:41:29 2024 +0100 Fixes redefined defines commit 58d25b8f582a4ba69ea6074a31ba33967bfb4ca1 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:37:55 2024 +0100 Adds python setup in the runner commit f675b6f4ef78a7e0669cc36ba1cdc9b1b8156446 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:24:50 2024 +0100 Update world.dm commit 7ced2c5e8c6797f72bb6a6725d57f045969aa2dd Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:17:12 2024 +0100 Should fix the segmentation fault commit bae3626fb1d91ab998289c2d46db020f58e0c027 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:11:56 2024 +0100 Adds auxmos to the unit tests commit 959991114ebd127d667c8bab3def93cc5b424e0d Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 14:02:40 2024 +0100 Removes 515 specific features commit ed73fdd6b165a5ee9ee1d9de63586df6dc3c7f85 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 13:57:28 2024 +0100 Removes all datum ignores commit 6b3f9e672cfa423c14fd45025ae8c40067df055b Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 13:55:45 2024 +0100 Ignores anything that has an initialize which requires parameters commit e4efb1a10cd0204cfe14c48a645907596468581d Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 13:55:03 2024 +0100 Update unit_test.dm commit a59438b686b40fe0144a692cc6051797a141dbf0 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 13:50:00 2024 +0100 Fixes compilation issues commit 7ed90ab3bea3837ea62b8663fac85a56b50b3e1e Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 13:46:04 2024 +0100 Ports some stuff relating to tests, but modularises ignore list for create_and_destroy commit 3e3b5408e23332250bd7c9463027e9d7ec3d7b36 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 13:02:00 2024 +0100 Update create_and_destroy.dm commit 584022f6ee07d84ab1c44d17e252ff1cb1673d9c Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:59:49 2024 +0100 Adds annotations commit 03c0b79f830db17196df47a7b5fe1474b29e28b5 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:51:03 2024 +0100 Update alternate_byond_versions.txt commit e19e30265956896b6ec098b0b9e94ee394626b0c Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:45:58 2024 +0100 Update alternate_byond_versions.txt commit 4ce3897b1f74735858a8955b8429b01ba4deb471 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:45:39 2024 +0100 Update alternate_byond_versions.txt commit 6ef031c179ac288b5c4f7601ea0c72d3ef988208 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:41:22 2024 +0100 Removes the need for the prefixed database commit 3704e380aa9e45eee79b1cf96c6d2ee827abbe92 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:38:19 2024 +0100 uses TG's repo for client compatability commit 289a8f1766154b72526df3e0c954e920dbedb842 Author: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat Aug 24 12:33:40 2024 +0100 Ports improved unit tests from TG --- .github/alternate_byond_versions.txt | 8 + .github/max_required_byond_client.txt | 8 + .github/workflows/ci_suite.yml | 293 ++++++++++++++++ .github/workflows/continuous_integration.yml | 139 -------- .github/workflows/rerun_flaky_tests.yml | 31 ++ .github/workflows/run_integration_tests.yml | 81 +++++ .../show_screenshot_test_results.yml | 43 +++ beestation.dme | 3 +- bin/temp-fast-test.cmd | 4 - code/__DEFINES/_helpers.dm | 3 + code/__DEFINES/directional.dm | 8 +- code/__DEFINES/qdel.dm | 45 ++- code/__DEFINES/zmimic.dm | 2 +- code/__HELPERS/qdel.dm | 8 - code/__HELPERS/test_helpers.dm | 35 ++ code/_globalvars/lists/flavor_misc.dm | 37 ++ code/_globalvars/logging.dm | 5 + code/_onclick/hud/alert.dm | 3 +- code/_onclick/hud/credits.dm | 4 + code/_onclick/hud/fullscreen.dm | 3 +- code/_onclick/hud/holoparasite.dm | 2 + code/_onclick/hud/hud.dm | 3 +- code/_onclick/hud/parallax.dm | 4 + .../hud/rendering/plane_master_controller.dm | 2 + code/_onclick/hud/screen_objects.dm | 8 + code/controllers/subsystem/garbage.dm | 293 +++++++++------- code/datums/brain_damage/imaginary_friend.dm | 2 + code/datums/brain_damage/mrat.dm | 2 + code/datums/brain_damage/split_personality.dm | 2 + code/datums/components/udder.dm | 2 + code/datums/datum.dm | 17 + code/datums/helper_datums/teleport.dm | 2 + code/datums/map_config.dm | 13 + code/datums/progressbar.dm | 7 + code/game/area/Space_Station_13_areas.dm | 5 +- code/game/area/area_color_correction.dm | 3 +- code/game/atoms.dm | 2 + code/game/gamemodes/gangs/gang_tags.dm | 2 + code/game/gamemodes/gangs/implant_gang.dm | 4 + code/game/gamemodes/meteor/meteors.dm | 4 + code/game/gamemodes/sandbox/h_sandbox.dm | 4 +- code/game/machinery/buttons.dm | 2 + code/game/machinery/camera/camera.dm | 2 + code/game/machinery/camera/camera_assembly.dm | 2 + code/game/machinery/computer/arena.dm | 2 + code/game/machinery/computer/crew.dm | 2 + code/game/machinery/computer/security.dm | 2 + code/game/machinery/doors/windowdoor.dm | 2 + code/game/machinery/doppler_array.dm | 2 + code/game/machinery/firealarm.dm | 2 + code/game/machinery/flasher.dm | 2 + code/game/machinery/launch_pad.dm | 4 + .../newscaster/newscaster_machine.dm | 2 + code/game/machinery/pipe/construction.dm | 2 + .../machinery/porta_turret/portable_turret.dm | 2 + code/game/machinery/shieldgen.dm | 2 + code/game/machinery/status_display.dm | 4 + .../machinery/telecomms/computers/message.dm | 2 + code/game/objects/effects/alien_acid.dm | 2 + .../objects/effects/anomalies/_anomalies.dm | 2 + .../objects/effects/anomalies/anomaly_flux.dm | 2 + .../effects/anomalies/anomaly_gravity.dm | 4 + code/game/objects/effects/contraband.dm | 2 + code/game/objects/effects/decals/cleanable.dm | 2 + .../effects/decals/cleanable/humans.dm | 6 + .../objects/effects/decals/cleanable/misc.dm | 2 + code/game/objects/effects/decals/crayon.dm | 2 + code/game/objects/effects/forcefields.dm | 4 + code/game/objects/effects/icons.dm | 4 + code/game/objects/effects/info.dm | 2 + code/game/objects/effects/landmarks.dm | 2 + code/game/objects/effects/lighting.dm | 4 + code/game/objects/effects/portals.dm | 4 + code/game/objects/effects/proximity.dm | 2 + .../objects/effects/spawners/gibspawner.dm | 2 + .../objects/effects/spawners/roomspawner.dm | 77 ++-- .../effects/temporary_visuals/clockwork.dm | 4 + .../objects/effects/temporary_visuals/cult.dm | 2 + .../temporary_visuals/miscellaneous.dm | 32 ++ .../projectiles/projectile_effects.dm | 2 + .../temporary_visuals/temporary_visual.dm | 4 +- code/game/objects/effects/wanted_poster.dm | 6 + code/game/objects/items/RPD.dm | 1 + code/game/objects/items/chrono_eraser.dm | 2 + code/game/objects/items/cigs_lighters.dm | 2 + .../circuitboards/machine_circuitboards.dm | 2 + code/game/objects/items/crab17.dm | 4 + code/game/objects/items/crayons.dm | 2 + code/game/objects/items/credit_holochip.dm | 2 + code/game/objects/items/devices/flashlight.dm | 4 + .../items/devices/forcefieldprojector.dm | 2 + .../objects/items/devices/radio/intercom.dm | 4 + code/game/objects/items/devices/scanners.dm | 2 + code/game/objects/items/documents.dm | 2 + .../objects/items/grenades/clusterbuster.dm | 4 + .../objects/items/grenades/discogrenade.dm | 2 + .../objects/items/implants/implant_track.dm | 2 +- .../objects/items/implants/implantuplink.dm | 4 + code/game/objects/items/mjolnir.dm | 4 + code/game/objects/items/spear.dm | 2 + code/game/objects/items/stacks/cash.dm | 2 + code/game/objects/items/stacks/medical.dm | 2 + code/game/objects/items/stacks/ores/ore.dm | 2 + code/game/objects/items/stacks/rods/rods.dm | 2 + .../items/stacks/sheets/mineral/metals.dm | 4 + .../items/stacks/sheets/organic/leather.dm | 2 + .../objects/items/stacks/sheets/sheets.dm | 2 + code/game/objects/items/stacks/stack.dm | 2 + code/game/objects/items/stacks/tiles/light.dm | 2 + .../objects/items/stacks/tiles/tile_types.dm | 2 + code/game/objects/objs.dm | 2 + code/game/objects/structures/artstuff.dm | 2 + code/game/objects/structures/bot_elevator.dm | 2 + .../crates_lockers/crates/secure.dm | 2 + code/game/objects/structures/displaycase.dm | 2 + code/game/objects/structures/extinguisher.dm | 2 + .../objects/structures/ghost_role_spawners.dm | 6 + code/game/objects/structures/ladders.dm | 2 + code/game/objects/structures/mirror.dm | 2 + .../objects/structures/petrified_statue.dm | 2 + code/game/objects/structures/tables_racks.dm | 2 + .../structures/transit_tubes/transit_tube.dm | 2 + .../objects/structures/windoor_assembly.dm | 2 + code/game/objects/structures/window.dm | 2 + code/game/say.dm | 2 + code/game/turfs/baseturf_skipover.dm | 4 + code/game/turfs/closed/_closed.dm | 2 + code/game/turfs/open/_open.dm | 2 + code/game/turfs/open/openspace.dm | 2 + code/game/turfs/turf.dm | 3 + code/game/turfs/turf_texture.dm | 2 + code/game/world.dm | 2 +- .../debug_variable_appearance.dm | 2 + code/modules/antagonists/blob/blob_mobs.dm | 2 + code/modules/antagonists/blob/overmind.dm | 2 + .../antagonists/blob/structures/_blob.dm | 2 + .../antagonists/blob/structures/core.dm | 2 + .../antagonists/blob/structures/factory.dm | 2 + .../changeling/powers/mutations.dm | 4 + .../clock_cult/clockwork_massive.dm | 2 + .../antagonists/clock_cult/clockwork_turfs.dm | 2 + code/modules/antagonists/cult/blood_magic.dm | 2 + code/modules/antagonists/cult/runes.dm | 10 + .../antagonists/heretic/magic/mansus_grasp.dm | 2 + .../heretic/structures/carving_knife.dm | 2 + .../antagonists/wizard/equipment/artefact.dm | 2 + .../atmospherics/environmental/LINDA_fire.dm | 2 + .../atmospherics/machinery/airalarm.dm | 2 + .../atmospherics/machinery/other/meter.dm | 2 + .../machinery/portable/canister.dm | 2 + code/modules/awaymissions/corpse.dm | 2 + .../bluespace_anchor/bluespace_anchor.dm | 2 + .../bluespace_anchor_deployer.dm | 2 + code/modules/cargo/gondolapod.dm | 2 + code/modules/cargo/supplypod.dm | 10 + code/modules/client/client_colour.dm | 3 +- .../preference_character_preview.dm | 2 + .../modules/clothing/spacesuits/chronosuit.dm | 2 + code/modules/events/wormholes.dm | 2 + code/modules/fields/timestop.dm | 2 + code/modules/fields/turf_objects.dm | 2 + code/modules/flufftext/Hallucination.dm | 12 + code/modules/holoparasite/_holoparasite.dm | 2 + .../holoparasite/abilities/lesser/snares.dm | 4 + .../abilities/lesser/telepathy.dm | 2 + .../holoparasite/abilities/lesser/teleport.dm | 6 + .../holoparasite/abilities/major/assassin.dm | 2 + .../holoparasite/abilities/major/explosive.dm | 2 + .../holoparasite/abilities/major/scout.dm | 2 + .../holoparasite/holoparasite_builder.dm | 2 + .../holoparasite/holoparasite_helpers.dm | 2 + code/modules/hydroponics/grown.dm | 4 + code/modules/hydroponics/grown/flowers.dm | 4 + .../modules/hydroponics/grown/grass_carpet.dm | 2 + code/modules/hydroponics/grown/misc.dm | 2 + code/modules/hydroponics/grown/mushrooms.dm | 2 + code/modules/hydroponics/grown/nettle.dm | 4 + code/modules/hydroponics/growninedible.dm | 2 + code/modules/hydroponics/seeds.dm | 2 + code/modules/keybindings/focus.dm | 3 +- code/modules/lighting/emissive_blocker.dm | 2 + code/modules/mapping/mapping_helpers.dm | 3 +- code/modules/mapping/merge_conflicts.dm | 2 + .../mining/equipment/marker_beacons.dm | 2 + code/modules/mining/equipment/resonator.dm | 2 + .../mining/lavaland/necropolis_chests.dm | 4 + code/modules/mob/living/carbon/carbon.dm | 2 + code/modules/mob/living/carbon/human/human.dm | 2 + .../carbon/human/species_types/dullahan.dm | 2 + .../carbon/human/species_types/golems.dm | 2 + .../mob/living/carbon/monkey/monkey.dm | 2 + code/modules/mob/living/inhand_holder.dm | 4 + code/modules/mob/living/silicon/ai/ai.dm | 4 + .../mob/living/simple_animal/bot/atmosbot.dm | 2 + .../mob/living/simple_animal/bot/ed209bot.dm | 2 + .../mob/living/simple_animal/bot/floorbot.dm | 2 + .../mob/living/simple_animal/bot/medbot.dm | 2 + .../living/simple_animal/heretic_monsters.dm | 4 + .../hostile/megafauna/blood_drunk_miner.dm | 2 + .../hostile/megafauna/colossus.dm | 2 + .../simple_animal/hostile/megafauna/drake.dm | 6 + .../hostile/megafauna/hierophant.dm | 10 + .../mob/living/simple_animal/hostile/mimic.dm | 2 + .../hostile/mining_mobs/elites/elite.dm | 2 + .../mining_mobs/elites/goliath_broodmother.dm | 2 + .../hostile/mining_mobs/goliath.dm | 6 +- .../living/simple_animal/hostile/statue.dm | 2 + .../living/simple_animal/hostile/stickman.dm | 2 + .../mob/living/simple_animal/slime/slime.dm | 6 + code/modules/mob/mob_defines.dm | 2 + .../computers/item/processor.dm | 3 + .../hardware/battery_module.dm | 2 + code/modules/multiz/zmimic/mimic_movable.dm | 8 +- code/modules/paperwork/contract.dm | 2 + code/modules/paperwork/origami.dm | 2 + .../byond_particles/emitter/smoke.dm | 2 + code/modules/photography/photos/frame.dm | 2 + code/modules/photography/photos/photo.dm | 2 + code/modules/plumbing/ducts.dm | 4 + .../plumbing/plumbers/_plumb_machinery.dm | 8 + code/modules/plumbing/plumbers/acclimator.dm | 2 + .../plumbing/plumbers/bottle_dispenser.dm | 2 + code/modules/plumbing/plumbers/destroyer.dm | 2 + code/modules/plumbing/plumbers/filter.dm | 2 + .../plumbing/plumbers/grinder_chemical.dm | 2 + .../plumbing/plumbers/reaction_chamber.dm | 2 + code/modules/plumbing/plumbers/splitters.dm | 2 + code/modules/plumbing/plumbers/synthesizer.dm | 2 + code/modules/point/point.dm | 2 + code/modules/power/cable.dm | 4 + code/modules/power/cell.dm | 2 + .../modules/power/lighting/light_construct.dm | 2 + code/modules/power/singularity/singularity.dm | 4 + code/modules/power/solar.dm | 2 + code/modules/power/tesla/energy_ball.dm | 2 + code/modules/power/tracker.dm | 2 + .../projectiles/ammunition/energy/portal.dm | 2 + .../projectiles/guns/misc/blastcannon.dm | 2 + .../projectile/special/wormhole.dm | 2 + code/modules/reagents/reagent_containers.dm | 2 + .../reagent_containers/chem_heirloom.dm | 2 + code/modules/recycling/conveyor.dm | 10 +- code/modules/recycling/disposal/bin.dm | 4 + .../recycling/disposal/construction.dm | 2 + code/modules/recycling/disposal/holder.dm | 1 + code/modules/recycling/disposal/outlet.dm | 2 + code/modules/recycling/disposal/pipe.dm | 2 + .../recycling/disposal/pipe_sorting.dm | 35 +- .../xenobiology/crossbreeding/_clothing.dm | 2 + .../xenobiology/crossbreeding/_structures.dm | 2 + .../xenobiology/crossbreeding/gentle.dm | 2 + .../xenobiology/crossbreeding/recurring.dm | 3 + code/modules/security/genpop.dm | 4 + .../bluespace_shuttle_pod/pod_computer.dm | 2 + code/modules/shuttle/ripple.dm | 2 + code/modules/shuttle/shuttle.dm | 2 + code/modules/shuttle/special.dm | 2 + .../loot/alien_artifact.dm | 2 + .../loot/artifact_defenses.dm | 2 + .../objective_computer.dm | 2 + .../shuttle_components/shuttle_console.dm | 2 + .../shuttle_components/shuttle_docking.dm | 4 + code/modules/spells/spell_types/forcewall.dm | 2 + code/modules/spells/spell_types/godhand.dm | 4 + .../spells/spell_types/lesserlichdom.dm | 2 + code/modules/spells/spell_types/lichdom.dm | 2 + code/modules/spells/spell_types/shapeshift.dm | 2 + .../spells/spell_types/touch_attacks.dm | 2 + code/modules/station_goals/bsa.dm | 2 + code/modules/surgery/organs/eyes.dm | 2 + .../unit_tests/{ => _DEFINES}/_unit_tests.dm | 131 +++---- code/modules/unit_tests/armour_checks.dm | 2 + code/modules/unit_tests/create_and_destroy.dm | 156 +++----- .../unit_tests/mapping/check_active_turfs.dm | 6 + .../unit_tests/mapping/check_area_apc.dm | 10 + .../mapping/check_camera_attachment.dm | 8 + .../unit_tests/mapping/check_disposals.dm | 93 +++++ .../mapping/check_light_attachment.dm | 10 + code/modules/unit_tests/mapping/map_test.dm | 60 ++++ code/modules/unit_tests/unit_test.dm | 168 +++++++-- code/modules/uplink/uplink_devices.dm | 12 + code/modules/vehicles/mecha/combat/durand.dm | 2 + code/modules/vehicles/mecha/mecha_wreckage.dm | 2 + .../wiremod/shell/brain_computer_interface.dm | 2 + .../traits/xenoartifact_minors.dm | 4 + code/modules/xenoarchaeology/xenoartifact.dm | 6 + tools/ci/annotate_dm.sh | 4 + tools/ci/annotate_od.sh | 4 + tools/ci/check_grep.sh | 332 ++++++++++++++---- tools/ci/ci_dependencies.sh | 6 + tools/ci/install_byond.sh | 5 +- tools/ci/install_ripgrep.sh | 6 + tools/ci/run_server.sh | 18 +- tools/dm_annotator/__main__.py | 51 +++ tools/expand_filedir_paths.py | 6 +- tools/od_annotator/__main__.py | 50 +++ .../schemas/beestation_dme.json | 10 + .../schemas/unit_tests.json | 9 + .../ticked_file_enforcement.py | 156 ++++++++ tools/trait_validity/check.py | 88 +++++ 300 files changed, 2680 insertions(+), 631 deletions(-) create mode 100644 .github/alternate_byond_versions.txt create mode 100644 .github/max_required_byond_client.txt create mode 100644 .github/workflows/ci_suite.yml delete mode 100644 .github/workflows/continuous_integration.yml create mode 100644 .github/workflows/rerun_flaky_tests.yml create mode 100644 .github/workflows/run_integration_tests.yml create mode 100644 .github/workflows/show_screenshot_test_results.yml delete mode 100644 bin/temp-fast-test.cmd create mode 100644 code/__HELPERS/test_helpers.dm rename code/modules/unit_tests/{ => _DEFINES}/_unit_tests.dm (55%) create mode 100644 code/modules/unit_tests/mapping/check_active_turfs.dm create mode 100644 code/modules/unit_tests/mapping/check_area_apc.dm create mode 100644 code/modules/unit_tests/mapping/check_camera_attachment.dm create mode 100644 code/modules/unit_tests/mapping/check_disposals.dm create mode 100644 code/modules/unit_tests/mapping/check_light_attachment.dm create mode 100644 code/modules/unit_tests/mapping/map_test.dm create mode 100644 tools/ci/annotate_dm.sh create mode 100644 tools/ci/annotate_od.sh create mode 100644 tools/ci/ci_dependencies.sh create mode 100644 tools/ci/install_ripgrep.sh create mode 100644 tools/dm_annotator/__main__.py create mode 100644 tools/od_annotator/__main__.py create mode 100644 tools/ticked_file_enforcement/schemas/beestation_dme.json create mode 100644 tools/ticked_file_enforcement/schemas/unit_tests.json create mode 100644 tools/ticked_file_enforcement/ticked_file_enforcement.py create mode 100644 tools/trait_validity/check.py diff --git a/.github/alternate_byond_versions.txt b/.github/alternate_byond_versions.txt new file mode 100644 index 0000000000000..309df9b58ee5c --- /dev/null +++ b/.github/alternate_byond_versions.txt @@ -0,0 +1,8 @@ +# This file contains extra tests to run for specific BYOND versions. +# This is useful for making sure we maintain compatibility with both older and newer versions, +# while still having our main tests run on a guaranteed pinned version. + +# Format is version: map +# Example: +# 500.1337: runtimestation +#515.1627: runtimestation diff --git a/.github/max_required_byond_client.txt b/.github/max_required_byond_client.txt new file mode 100644 index 0000000000000..86c9b22d1b04c --- /dev/null +++ b/.github/max_required_byond_client.txt @@ -0,0 +1,8 @@ +# Highest byond client version allowed to be required by the byond world. Set to 9999 to disable the check flat out. +# If the compiled world requires clients use a version higher than this, ci will fail. +# for instance: if this is set to 514, and a pr uses a 515 client feature, an alert will trigger +# If you have to update this number for your pr, you should make it VERY CLEAR in the pr body that you did so. +# (Requiring clients update to connect to the game server is not something we like to spring on them with no notice, +# especially for beta builds where the pager/updater won't let them update without additional configuration.) + +514 diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml new file mode 100644 index 0000000000000..2c337da945a2b --- /dev/null +++ b/.github/workflows/ci_suite.yml @@ -0,0 +1,293 @@ +name: CI Suite +on: + push: + branches: + - master + - 'project/**' + - 'gh-readonly-queue/master/**' + - 'gh-readonly-queue/project/**' + pull_request: + branches: + - master + - 'project/**' + merge_group: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + run_linters: + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) + name: Run Linters + runs-on: ubuntu-22.04 + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + - name: Restore SpacemanDMM cache + uses: actions/cache@v4 + with: + path: ~/SpacemanDMM + key: ${{ runner.os }}-spacemandmm-${{ hashFiles('dependencies.sh') }} + restore-keys: | + ${{ runner.os }}-spacemandmm- + - name: Restore Yarn cache + uses: actions/cache@v4 + with: + path: tgui/.yarn/cache + key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Restore Node cache + uses: actions/cache@v4 + with: + path: ~/.nvm + key: ${{ runner.os }}-node-${{ hashFiles('dependencies.sh') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Restore Bootstrap cache + uses: actions/cache@v4 + with: + path: tools/bootstrap/.cache + key: ${{ runner.os }}-bootstrap-${{ hashFiles('tools/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-bootstrap- + - name: Restore Rust cache + uses: actions/cache@v4 + with: + path: ~/.cargo + key: ${{ runner.os }}-rust-${{ hashFiles('tools/ci/ci_dependencies.sh')}} + restore-keys: | + ${{ runner.os }}-rust- + - name: Restore Cutter cache + uses: actions/cache@v4 + with: + path: tools/icon_cutter/cache + key: ${{ runner.os }}-cutter-${{ hashFiles('dependencies.sh') }} + - name: Python setup + uses: actions/setup-python@v4 + with: + python-version: "3.11" + - name: Install OpenDream + uses: robinraju/release-downloader@v1.9 + with: + repository: "OpenDreamProject/OpenDream" + tag: "latest" + fileName: "DMCompiler_linux-x64.tar.gz" + extract: true + - name: Install Tools + run: | + pip3 install setuptools + bash tools/ci/install_node.sh + bash tools/ci/install_spaceman_dmm.sh dreamchecker + bash tools/ci/install_ripgrep.sh + tools/bootstrap/python -c '' + - name: Setup linters + id: linter-setup + run: ':' + - name: Run Grep Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_grep.sh + - name: Ticked File Enforcement + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: | + tools/bootstrap/python tools/ticked_file_enforcement/ticked_file_enforcement.py < tools/ticked_file_enforcement/schemas/beestation_dme.json + tools/bootstrap/python tools/ticked_file_enforcement/ticked_file_enforcement.py < tools/ticked_file_enforcement/schemas/unit_tests.json + - name: Check Define Sanity + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m define_sanity.check + - name: Check Trait Validity + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m trait_validity.check + - name: Run DreamChecker + if: steps.linter-setup.conclusion == 'success' && !cancelled() + shell: bash + run: ~/dreamchecker 2>&1 | bash tools/ci/annotate_dm.sh + - name: Run OpenDream + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: ./DMCompiler_linux-x64/DMCompiler beestation.dme --suppress-unimplemented --define=CIBUILDING | bash tools/ci/annotate_od.sh + - name: Run Map Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: | + tools/bootstrap/python -m mapmerge2.dmm_test + tools/bootstrap/python -m tools.maplint.source + - name: Check Cutter + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m tools.icon_cutter.check + - name: Run DMI Tests + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m dmi.test + - name: Check File Directories + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_filedirs.sh beestation.dme + - name: Check Changelogs + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_changelogs.sh + - name: Check Miscellaneous Files + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_misc.sh + - name: Run TGUI Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/build/build --ci lint tgui-test + + compile_all_maps: + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) + name: Compile Maps + needs: [collect_data] + runs-on: ubuntu-22.04 + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + - name: Restore BYOND cache + uses: actions/cache@v4 + with: + path: ~/BYOND + key: ${{ runner.os }}-byond-${{ hashFiles('dependencies.sh') }} + - name: Compile All Maps + run: | + bash tools/ci/install_byond.sh + source $HOME/BYOND/byond/bin/byondsetup + tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS + - name: Check client Compatibility + uses: tgstation/byond-client-compatibility-check@v3 + with: + dmb-location: beestation.dmb + max-required-client-version: ${{needs.collect_data.outputs.max_required_byond_client}} + + collect_data: + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) + name: Collect data for other tasks + runs-on: ubuntu-22.04 + timeout-minutes: 5 + outputs: + maps: ${{ steps.map_finder.outputs.maps }} + alternate_tests: ${{ steps.alternate_test_finder.outputs.alternate_tests }} + max_required_byond_client: ${{ steps.max_required_byond_client.outputs.max_required_byond_client }} + + steps: + - uses: actions/checkout@v4 + - name: Find Maps + id: map_finder + run: | + echo "$(ls -mw0 _maps/*.json)" > maps_output.txt + sed -i -e s+_maps/+\"+g -e s+.json+\"+g maps_output.txt + echo "Maps: $(cat maps_output.txt)" + echo "maps={\"paths\":[$(cat maps_output.txt)]}" >> $GITHUB_OUTPUT + - name: Find Alternate Tests + id: alternate_test_finder + run: | + ALTERNATE_TESTS_JSON=$(jq -nRc '[inputs | capture("^(?[0-9]+)\\.(?[0-9]+): (?.+)$")]' .github/alternate_byond_versions.txt) + echo "alternate_tests=$ALTERNATE_TESTS_JSON" >> $GITHUB_OUTPUT + - name: Collect byond client version configuration + id: max_required_byond_client + #the regex here does not filter out non-numbers because error messages about no input are less helpful then error messages about bad input (which includes the bad input) + run: | + echo "max_required_byond_client=$(grep -Ev '^[[:blank:]]{0,}#{1,}|^[[:blank:]]{0,}$' .github/max_required_byond_client.txt | tail -n1)" >> $GITHUB_OUTPUT + + run_all_tests: + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) + name: Integration Tests + needs: [collect_data] + + strategy: + fail-fast: false + matrix: + map: ${{ fromJSON(needs.collect_data.outputs.maps).paths }} + + uses: ./.github/workflows/run_integration_tests.yml + with: + map: ${{ matrix.map }} + max_required_byond_client: ${{needs.collect_data.outputs.max_required_byond_client}} + + run_alternate_tests: + if: ( !contains(github.event.head_commit.message, '[ci skip]') && needs.collect_data.outputs.alternate_tests != '[]' ) + name: Alternate Tests + needs: [collect_data] + strategy: + fail-fast: false + matrix: + setup: ${{ fromJSON(needs.collect_data.outputs.alternate_tests) }} + + uses: ./.github/workflows/run_integration_tests.yml + with: + map: ${{ matrix.setup.map }} + major: ${{ matrix.setup.major }} + minor: ${{ matrix.setup.minor }} + max_required_byond_client: ${{needs.collect_data.outputs.max_required_byond_client}} + + check_alternate_tests: + if: ( !contains(github.event.head_commit.message, '[ci skip]') && needs.collect_data.outputs.alternate_tests != '[]' ) + name: Check Alternate Tests + needs: [run_alternate_tests] + runs-on: ubuntu-22.04 + timeout-minutes: 5 + steps: + - run: echo Alternate tests passed. + + compare_screenshots: + if: ( !contains(github.event.head_commit.message, '[ci skip]') && (always() && (!failure() && !cancelled())) ) + needs: [run_all_tests, run_alternate_tests] + name: Compare Screenshot Tests + timeout-minutes: 15 + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Setup directory + run: mkdir -p artifacts + # If we ever add more artifacts, this is going to break, but it'll be obvious. + - name: Download screenshot tests + uses: actions/download-artifact@v4 + with: + path: artifacts + - name: ls -R + run: ls -R artifacts + - name: Setup screenshot comparison + run: npm i + working-directory: tools/screenshot-test-comparison + - name: Run screenshot comparison + run: node tools/screenshot-test-comparison/index.js artifacts code/modules/unit_tests/screenshots artifacts/screenshot_comparisons + # workflow_run does not give you the PR it ran on, + # even through the thing literally named "matching pull requests". + # However, in GraphQL, you can check if the check suite was ran + # by a specific PR, so trusting the (user controlled) action here is okay, + # as long as we check it later in show_screenshot_test_results + - name: Save PR ID + if: failure() && github.event.pull_request + run: | + echo ${{ github.event.pull_request.number }} > artifacts/screenshot_comparisons/pull_request_number.txt + - name: Upload bad screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: bad-screenshots + path: artifacts/screenshot_comparisons + + test_windows: + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) + name: Windows Build + needs: [collect_data] + runs-on: windows-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + - name: Restore Yarn cache + uses: actions/cache@v4 + with: + path: tgui/.yarn/cache + key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Compile + run: pwsh tools/ci/build.ps1 + env: + DM_EXE: "C:\\byond\\bin\\dm.exe" + - name: Check client Compatibility + uses: tgstation/byond-client-compatibility-check@v3 + with: + dmb-location: beestation.dmb + max-required-client-version: ${{needs.collect_data.outputs.max_required_byond_client}} diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml deleted file mode 100644 index d52ea0e45f3d5..0000000000000 --- a/.github/workflows/continuous_integration.yml +++ /dev/null @@ -1,139 +0,0 @@ -name: Run tests - -on: - workflow_dispatch: -# push: -# paths-ignore: -# - "html/changelogs/**" -# - "html/changelog.html" - pull_request: - branches: - - master - merge_group: - branches: - - master - -jobs: - run_linters: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Python setup - uses: actions/setup-python@v4 - with: - python-version: "3.11" - cache: 'pip' - - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: yarn - cache-dependency-path: tgui/yarn.lock - - name: Cache SpacemanDMM - id: cache-spacemandmm - uses: actions/cache@v3 - with: - path: ~/dreamchecker - key: ${{ runner.os }}-spacemandmm-cache-${{ hashFiles('dependencies.sh') }} - - name: Install SpacemanDMM - if: steps.cache-spacemandmm.outputs.cache-hit != 'true' - run: bash tools/ci/install_spaceman_dmm.sh dreamchecker - - name: Install Tools - run: | - pip install setuptools - pip install -r tools/requirements.txt - - name: Run Linters - run: | - bash tools/ci/check_filedirs.sh beestation.dme - bash tools/ci/check_changelogs.sh - find . -name "*.php" -print0 | xargs -0 -n1 php -l - find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python ./tools/json_verifier.py - bash tools/ci/check_grep.sh - tools/build/build --ci lint tgui-test - tools/bootstrap/python -m dmi.test - tools/bootstrap/python -m mapmerge2.dmm_test - ~/dreamchecker - compile_all_maps: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Setup cache - id: cache-byond - uses: actions/cache@v3 - with: - path: ~/BYOND - key: ${{ runner.os }}-byond-cache-${{ hashFiles('Dockerfile') }} - - name: Install BYOND - if: steps.cache-byond.outputs.cache-hit != 'true' - run: bash tools/ci/install_byond.sh - - name: Compile All Maps - run: | - source $HOME/BYOND/byond/bin/byondsetup - python3 tools/ci/template_dm_generator.py - tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS - run_all_tests: - runs-on: ubuntu-20.04 - services: - mariadb: - image: mariadb:latest - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306 - options: --health-cmd="mariadb-admin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: yarn - cache-dependency-path: tgui/yarn.lock - - name: Cache BYOND - id: cache-byond - uses: actions/cache@v3 - with: - path: ~/BYOND - key: ${{ runner.os }}-byond-cache-${{ hashFiles('Dockerfile') }} - - name: Install BYOND - if: steps.cache-byond.outputs.cache-hit != 'true' - run: bash tools/ci/install_byond.sh - - name: Setup database - run: | - sudo systemctl start mysql - mysql -u root -proot -e 'CREATE DATABASE bee_ci;' - mysql -u root -proot bee_ci < SQL/beestation_schema.sql - - name: Install rust-g - run: | - sudo dpkg --add-architecture i386 - sudo apt update || true - sudo apt install libssl1.1:i386 - bash tools/ci/install_rust_g.sh - - name: Install auxmos - run: | - bash tools/ci/install_auxmos.sh - - name: Compile Tests - run: | - source $HOME/BYOND/byond/bin/byondsetup - tools/build/build --ci dm -DCIBUILDING -DANSICOLORS - - name: Run Tests - run: | - source $HOME/BYOND/byond/bin/byondsetup - bash tools/ci/run_server.sh - test_windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: yarn - cache-dependency-path: tgui/yarn.lock - - name: Cache BYOND - id: cache-byond - uses: actions/cache@v3 - with: - path: C:/byond - key: ${{ runner.os }}-byond-cache-${{ hashFiles('dependencies.sh') }} - - name: Compile - run: pwsh tools/ci/build.ps1 - env: - DM_EXE: "C:\\byond\\bin\\dm.exe" diff --git a/.github/workflows/rerun_flaky_tests.yml b/.github/workflows/rerun_flaky_tests.yml new file mode 100644 index 0000000000000..7f498de144308 --- /dev/null +++ b/.github/workflows/rerun_flaky_tests.yml @@ -0,0 +1,31 @@ +name: Rerun/Report Flaky Tests +on: + workflow_run: + workflows: [CI Suite] + types: + - completed +jobs: + rerun_flaky_tests: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Rerun flaky tests + uses: actions/github-script@v6 + with: + script: | + const { rerunFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js') + await rerunFlakyTests({ github, context }) + report_flaky_tests: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.run_attempt == 2 }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Report flaky tests + uses: actions/github-script@v6 + with: + script: | + const { reportFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js') + await reportFlakyTests({ github, context }) diff --git a/.github/workflows/run_integration_tests.yml b/.github/workflows/run_integration_tests.yml new file mode 100644 index 0000000000000..43eeda344d694 --- /dev/null +++ b/.github/workflows/run_integration_tests.yml @@ -0,0 +1,81 @@ +# This is a reusable workflow to run integration tests on a single map. +# This is run for every single map in ci_suite.yml. You might want to edit that instead. +name: Run Integration Tests +on: + workflow_call: + inputs: + map: + required: true + type: string + major: + required: false + type: string + minor: + required: false + type: string + max_required_byond_client: + required: true + type: string + +jobs: + run_integration_tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + services: + mysql: + image: mysql:latest + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - uses: actions/checkout@v4 + - name: Restore BYOND cache + uses: actions/cache@v4 + with: + path: ~/BYOND + key: ${{ runner.os }}-byond-${{ hashFiles('dependencies.sh') }} + - name: Setup database + run: | + sudo systemctl start mysql + mysql -u root -proot -e 'CREATE DATABASE bee_ci;' + mysql -u root -proot bee_ci < SQL/beestation_schema.sql + - name: Install rust-g + run: | + bash tools/ci/install_rust_g.sh +# Bee: Dreamlua is not used +# - name: Install dreamluau +# run: | +# bash tools/ci/install_dreamluau.sh + - name: Install auxmos + run: | + bash tools/ci/install_auxmos.sh + - name: Configure version + run: | + echo "BYOND_MAJOR=${{ inputs.major }}" >> $GITHUB_ENV + echo "BYOND_MINOR=${{ inputs.minor }}" >> $GITHUB_ENV + if: ${{ inputs.major }} + - name: Compile Tests + id: compile_tests + run: | + bash tools/ci/install_byond.sh + source $HOME/BYOND/byond/bin/byondsetup + tools/build/build --ci dm -DCIBUILDING -DANSICOLORS -Werror -ITG0001 -I"loop_checks" + - name: Run Tests + run: | + source $HOME/BYOND/byond/bin/byondsetup + bash tools/ci/run_server.sh ${{ inputs.map }} + - name: Upload screenshot tests + if: always() + uses: actions/upload-artifact@v4 + with: + name: test_artifacts_${{ inputs.map }}_${{ inputs.major }}_${{ inputs.minor }} + path: data/screenshots_new/ + retention-days: 1 + - name: Check client Compatibility + if: always() && steps.compile_tests.outcome == 'success' + uses: tgstation/byond-client-compatibility-check@v3 + with: + dmb-location: beestation.dmb + max-required-client-version: ${{inputs.max_required_byond_client}} diff --git a/.github/workflows/show_screenshot_test_results.yml b/.github/workflows/show_screenshot_test_results.yml new file mode 100644 index 0000000000000..c61d09fa89057 --- /dev/null +++ b/.github/workflows/show_screenshot_test_results.yml @@ -0,0 +1,43 @@ +# This is a separate workflow so that it can access secrets, which are necessary +# because we need to be able to upload the images and post a comment. +# In the event this workflow fails, the screenshot test results are still +# available as an artifact of the screenshot test comparison workflow itself. +# This simply provides necessary quality of life. +name: Show Screenshot Test Results +on: + workflow_run: + workflows: [CI Suite] + types: + - completed +jobs: + show_screenshot_test_results: + if: ( !contains(github.event.head_commit.message, '[ci skip]') && github.event.workflow_run.run_attempt == 1 ) + name: Show Screenshot Test Results + runs-on: ubuntu-22.04 + steps: + - name: "Check for ARTIFACTS_FILE_HOUSE_KEY" + id: secrets_set + env: + ENABLER_SECRET: ${{ secrets.ARTIFACTS_FILE_HOUSE_KEY }} + run: | + unset SECRET_EXISTS + if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi + echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT + - name: Checkout + if: steps.secrets_set.outputs.SECRETS_ENABLED + uses: actions/checkout@v4 + - name: Prepare module + if: steps.secrets_set.outputs.SECRETS_ENABLED + run: | + # This is needed because node-fetch needs import and doesn't work with require :/ + echo "{\"type\": \"module\"}" > package.json + npm install node-fetch + - name: Show screenshot test results + if: steps.secrets_set.outputs.SECRETS_ENABLED + uses: actions/github-script@v6 + env: + FILE_HOUSE_KEY: ${{ secrets.ARTIFACTS_FILE_HOUSE_KEY }} + with: + script: | + const { showScreenshotTestResults } = await import('${{ github.workspace }}/tools/ci/show_screenshot_test_results.js') + await showScreenshotTestResults({ github, context, exec }) diff --git a/beestation.dme b/beestation.dme index 6dbf80dfaa37b..82028b1031a78 100644 --- a/beestation.dme +++ b/beestation.dme @@ -290,6 +290,7 @@ #include "code\__HELPERS\stoplag.dm" #include "code\__HELPERS\string_assoc_lists.dm" #include "code\__HELPERS\string_lists.dm" +#include "code\__HELPERS\test_helpers.dm" #include "code\__HELPERS\text.dm" #include "code\__HELPERS\time.dm" #include "code\__HELPERS\turfs.dm" @@ -4097,7 +4098,7 @@ #include "code\modules\tgui_panel\telemetry.dm" #include "code\modules\tgui_panel\tgui_panel.dm" #include "code\modules\tooltip\tooltip.dm" -#include "code\modules\unit_tests\_unit_tests.dm" +#include "code\modules\unit_tests\_DEFINES\_unit_tests.dm" #include "code\modules\uplink\uplink_devices.dm" #include "code\modules\uplink\uplink_items.dm" #include "code\modules\uplink\uplink_purchase_log.dm" diff --git a/bin/temp-fast-test.cmd b/bin/temp-fast-test.cmd deleted file mode 100644 index f604245f8c27d..0000000000000 --- a/bin/temp-fast-test.cmd +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -:: Nuke this task once the Del the World unit test passes. This exists to fix those errors easily. - -call "%~dp0\..\tools\build\build.bat" --wait-on-error dm-test -DREFERENCE_TRACKING_FAST %* diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm index 5fc95acb1af11..5f6fb4d9af78d 100644 --- a/code/__DEFINES/_helpers.dm +++ b/code/__DEFINES/_helpers.dm @@ -12,3 +12,6 @@ // Custom types that we define don't get a unique id, but this is useful for identifying // types that don't normally have a way to run istype() on them. #define TYPEID(thing) copytext(REF(thing), 4, 6) + +/// Takes a datum as input, returns its ref string +#define text_ref(datum) ref(datum) diff --git a/code/__DEFINES/directional.dm b/code/__DEFINES/directional.dm index bfde544b44716..14d4ae1d7f17d 100644 --- a/code/__DEFINES/directional.dm +++ b/code/__DEFINES/directional.dm @@ -16,8 +16,14 @@ /// Inverse direction, taking into account UP|DOWN if necessary. #define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) +/obj/var/_reflection_is_directional = FALSE + /// Create directional subtypes for a path to simplify mapping. -#define MAPPING_DIRECTIONAL_HELPERS(path, offset) ##path/directional/north {\ +#define MAPPING_DIRECTIONAL_HELPERS(path, offset)\ +##path {\ + _reflection_is_directional = TRUE;\ +} \ +##path/directional/north {\ dir = NORTH; \ pixel_y = offset; \ } \ diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 039658baad9dc..419b0c1176eeb 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -1,20 +1,27 @@ -//defines that give qdel hints. these can be given as a return in destory() or by calling +//! Defines that give qdel hints. +//! +//! These can be given as a return in [/atom/proc/Destroy] or by calling [/proc/qdel]. +/// `qdel` should queue the object for deletion. +#define QDEL_HINT_QUEUE 0 +/// `qdel` should let the object live after calling [/atom/proc/Destroy]. +#define QDEL_HINT_LETMELIVE 1 +/// Functionally the same as the above. `qdel` should assume the object will gc on its own, and not check it. +#define QDEL_HINT_IWILLGC 2 +/// Qdel should assume this object won't GC, and queue a hard delete using a hard reference. +#define QDEL_HINT_HARDDEL 3 +// Qdel should assume this object won't gc, and hard delete it posthaste. +#define QDEL_HINT_HARDDEL_NOW 4 -#define QDEL_HINT_QUEUE 0 //qdel should queue the object for deletion. -#define QDEL_HINT_LETMELIVE 1 //qdel should let the object live after calling destory. -#define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it. -#define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference. -#define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste. -//defines for the gc_destroyed var #ifdef REFERENCE_TRACKING /** If REFERENCE_TRACKING is enabled, qdel will call this object's find_references() verb. * * Functionally identical to [QDEL_HINT_QUEUE] if [GC_FAILURE_HARD_LOOKUP] is not enabled in _compiler_options.dm. */ -#define QDEL_HINT_FINDREFERENCE 5 -/// Behavior as QDEL_HINT_FINDREFERENCE, but only if the GC fails and a hard delete is forced. +#warn TG0001 qdel REFERENCE_TRACKING enabled +#define QDEL_HINT_FINDREFERENCE 5 +/// Behavior as [QDEL_HINT_FINDREFERENCE], but only if the GC fails and a hard delete is forced. #define QDEL_HINT_IFFAIL_FINDREFERENCE 6 #endif @@ -24,19 +31,37 @@ #define GC_QUEUE_HARDDELETE 3 //! short queue for things that hard delete instead of going thru the gc subsystem, this is purely so if they *can* softdelete, they will soft delete rather then wasting time with a hard delete. #define GC_QUEUE_COUNT 3 //! Number of queues, used for allocating the nested lists. Don't forget to increase this if you add a new queue stage + +// Defines for the ssgarbage queue items +#define GC_QUEUE_ITEM_QUEUE_TIME 1 //! Time this item entered the queue +#define GC_QUEUE_ITEM_REF 2 //! Ref to the item +#define GC_QUEUE_ITEM_GCD_DESTROYED 3 //! Item's gc_destroyed var value. Used to detect ref reuse. +#define GC_QUEUE_ITEM_INDEX_COUNT 3 //! Number of item indexes, used for allocating the nested lists. Don't forget to increase this if you add a new queue item index + // Defines for the time an item has to get its reference cleaned before it fails the queue and moves to the next. #define GC_FILTER_QUEUE (1 SECONDS) #define GC_CHECK_QUEUE (5 MINUTES) #define GC_DEL_QUEUE (10 SECONDS) + #define QDEL_ITEM_ADMINS_WARNED (1<<0) //! Set when admins are told about lag causing qdels in this type. #define QDEL_ITEM_SUSPENDED_FOR_LAG (1<<1) //! Set when a type can no longer be hard deleted on failure because of lag it causes while this happens. // Defines for the [gc_destroyed][/datum/var/gc_destroyed] var. -#define GC_QUEUED_FOR_QUEUING -1 #define GC_CURRENTLY_BEING_QDELETED -2 #define QDELING(X) (X.gc_destroyed) #define QDELETED(X) (isnull(X) || QDELING(X)) #define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) +// This is a bit hacky, we do it to avoid people relying on a return value for the macro +// If you need that you should use QDEL_IN_STOPPABLE instead +#define QDEL_IN(item, time) ; \ + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time); +#define QDEL_IN_STOPPABLE(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time, TIMER_STOPPABLE) +#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) +#define QDEL_NULL(item) qdel(item); item = null +#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } +#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(______qdel_list_wrapper), L), time, TIMER_STOPPABLE) +#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } +#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } diff --git a/code/__DEFINES/zmimic.dm b/code/__DEFINES/zmimic.dm index a56e30be3638b..685a6870ec51c 100644 --- a/code/__DEFINES/zmimic.dm +++ b/code/__DEFINES/zmimic.dm @@ -1,6 +1,6 @@ #define TURF_IS_MIMICKING(T) (isturf(T) && (T:z_flags & Z_MIMIC_BELOW)) -#define CHECK_OO_EXISTENCE(OO) if (OO && !MOVABLE_IS_ON_ZTURF(OO) && !OO:destruction_timer) { OO:destruction_timer = QDEL_IN(OO, 10 SECONDS); } +#define CHECK_OO_EXISTENCE(OO) if (OO && !MOVABLE_IS_ON_ZTURF(OO) && !OO:destruction_timer) { OO:destruction_timer = QDEL_IN_STOPPABLE(OO, 10 SECONDS); } #define UPDATE_OO_IF_PRESENT CHECK_OO_EXISTENCE(src:bound_overlay); if (src:bound_overlay) { update_above(); } // These aren't intended to be used anywhere else, they just can't be undef'd because DM is dum. diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index ba31b067c4aa5..738c2c4e62d76 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -1,10 +1,2 @@ -#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE) -#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) -#define QDEL_NULL(item) qdel(item); item = null -#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } -#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(______qdel_list_wrapper), L), time, TIMER_STOPPABLE) -#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } -#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } - /proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. QDEL_LIST(L) diff --git a/code/__HELPERS/test_helpers.dm b/code/__HELPERS/test_helpers.dm new file mode 100644 index 0000000000000..759fe6a1fb71d --- /dev/null +++ b/code/__HELPERS/test_helpers.dm @@ -0,0 +1,35 @@ +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) + +/// Builds (and returns) a list of atoms that we shouldn't initialize in generic testing, like Create and Destroy. +/// It is appreciated to add the reason why the atom shouldn't be initialized if you add it to this list. +/datum/unit_test/proc/build_list_of_uncreatables() + RETURN_TYPE(/list) + var/list/output = list() + for (var/type in subtypesof(/datum/ignore_type)) + var/datum/ignore_type/temp = new() + temp.add_ignores(output) + return output + +// Extension procs crash byond with enough of them due to stack overflows, this allows us to do it +// without traversing the stack +/datum/ignore_type/proc/add_ignores(list/target) + return + +#define CREATION_TEST_IGNORE_SELF(path)/datum/ignore_type/##path/add_ignores(list/target) {\ + target += path;\ +} + +#define CREATION_TEST_IGNORE_SUBTYPES(path)/datum/ignore_type/##path/add_ignores(list/target) {\ + target += typesof(path);\ +} + +// Annoyingly, dview is defined inside of _DEFINES, so we are doing it here +CREATION_TEST_IGNORE_SELF(/mob/dview) + +#else + +#define CREATION_TEST_IGNORE_SELF(path) + +#define CREATION_TEST_IGNORE_SUBTYPES(path) + +#endif diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index a70e44d4d7912..8f2166cbe19b0 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -312,6 +312,43 @@ GLOBAL_LIST_INIT(TAGGERLOCATIONS, list( "Detective's Office", )) +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) + +GLOBAL_LIST_INIT(tagger_destination_areas, list( + "Disposals" = list(/area/maintenance/disposal), + "Cargo Bay" = list(/area/quartermaster), + "QM Office" = list(/area/quartermaster/qm, /area/quartermaster/qm_bedroom), + "Engineering" = list(/area/engine, /area/engineering), + "CE Office" = list(/area/crew_quarters/heads/chief), + "Atmospherics" = list(/area/engine/atmos, /area/engine/atmospherics_engine), + "Security" = list(/area/security), + "HoS Office" = list(/area/crew_quarters/heads/hos), + "Medbay" = list(/area/medical), + "CMO Office" = list(/area/crew_quarters/heads/cmo), + "Chemistry" = list(/area/medical/chemistry), + "Research" = list(/area/science), + "RD Office" = list(/area/crew_quarters/heads/hor), + "Robotics" = list(/area/science/robotics), + "HoP Office" = list(/area/crew_quarters/heads/hop), + "Library" = list(/area/library), + "Chapel" = list(/area/chapel), + "Theatre" = list(/area/crew_quarters/theatre), + "Bar" = list(/area/crew_quarters/bar), + "Kitchen" = list(/area/crew_quarters/kitchen), + "Hydroponics" = list(/area/hydroponics), + "Janitor Closet" = list(/area/janitor), + "Genetics" = list(/area/medical/genetics), + "Testing Range" = list(/area/science/misc_lab, /area/science/test_area, /area/science/mixing), + "Toxins" = list(/area/science/misc_lab, /area/science/test_area, /area/science/mixing), + "Dormitories" = list(/area/crew_quarters/dorms, /area/commons/dorms), + "Virology" = list(/area/medical/virology), + "Xenobiology" = list(/area/science/xenobiology), + "Law Office" = list(/area/lawoffice), + "Detective's Office" = list(/area/security/detectives_office), +)) + +#endif + GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt") + "") GLOBAL_LIST_INIT(station_names, world.file2list("strings/station_names.txt") + "") diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index ea096240ae963..d706b5c99c5d1 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -122,3 +122,8 @@ GLOBAL_PROTECT(picture_logging_prefix) GLOBAL_LIST_EMPTY(harddel_log) GLOBAL_PROTECT(harddel_log) #endif + +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) +GLOBAL_VAR(test_log) +GLOBAL_PROTECT(test_log) +#endif diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index d9a94490bc3ea..6a2155bd9faf5 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -710,8 +710,7 @@ so as to remain in compliance with the most up-to-date laws." reorganize_alerts(M) return 1 -/mob - var/list/alerts = list() // contains /atom/movable/screen/alert only // On /mob so clientless mobs will throw alerts properly +/mob/var/list/alerts = list() // contains /atom/movable/screen/alert only // On /mob so clientless mobs will throw alerts properly /atom/movable/screen/alert/Click(location, control, params) if(!usr || !usr.client) diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm index 5ed1978c7b366..f322a18cfe3e4 100644 --- a/code/_onclick/hud/credits.dm +++ b/code/_onclick/hud/credits.dm @@ -60,6 +60,8 @@ GLOBAL_LIST(end_titles) plane = SPLASHSCREEN_PLANE var/matrix/target +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/credit) + /atom/movable/screen/credit/Initialize(mapload, credited) . = ..() maptext = MAPTEXT("[credited]") @@ -86,6 +88,8 @@ GLOBAL_LIST(end_titles) icon = 'icons/title_cards.dmi' screen_loc = "4,1" +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/credit/title_card) + /atom/movable/screen/credit/title_card/Initialize(mapload, credited, title_icon_state) icon_state = title_icon_state . = ..() diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index abcdedd7fe187..1ccbecf79a9f6 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -1,6 +1,5 @@ -/mob - var/list/screens = list() +/mob/var/list/screens = list() /mob/proc/overlay_fullscreen(category, type, severity) var/atom/movable/screen/fullscreen/screen = screens[category] diff --git a/code/_onclick/hud/holoparasite.dm b/code/_onclick/hud/holoparasite.dm index e339109028978..2445452baf928 100644 --- a/code/_onclick/hud/holoparasite.dm +++ b/code/_onclick/hud/holoparasite.dm @@ -117,6 +117,8 @@ var/static/list/mutable_appearance/timer_fraction_overlays COOLDOWN_DECLARE(timer) +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite) + /atom/movable/screen/holoparasite/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner) . = ..() if(!istype(_owner)) diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index c81b2e997f7ea..3eae770387a68 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -128,8 +128,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( return ..() -/mob - var/hud_type = /datum/hud +/mob/var/hud_type = /datum/hud /mob/proc/create_mob_hud() if(!client || hud_used) diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 3f404f6031ea0..78f02d8a9702c 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -264,6 +264,8 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/parallax_layer) + /atom/movable/screen/parallax_layer/Initialize(mapload, view) . = ..() if (!view) @@ -315,6 +317,8 @@ /atom/movable/screen/parallax_layer/random/space_gas icon_state = "random_layer1" +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/parallax_layer/random/space_gas) + /atom/movable/screen/parallax_layer/random/space_gas/Initialize(mapload, view) . = ..() src.add_atom_colour(SSparallax.assign_random_parallax_colour(), ADMIN_COLOUR_PRIORITY) diff --git a/code/_onclick/hud/rendering/plane_master_controller.dm b/code/_onclick/hud/rendering/plane_master_controller.dm index 9799ca8b9ec7f..46665788d9640 100644 --- a/code/_onclick/hud/rendering/plane_master_controller.dm +++ b/code/_onclick/hud/rendering/plane_master_controller.dm @@ -8,6 +8,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller) ///Ensures that all the planes are correctly in the controlled_planes list. +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/plane_master_controller) + /atom/movable/plane_master_controller/Initialize(mapload, datum/hud/hud) . = ..() if(!istype(hud)) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index d12383cc8c265..734cc2645047f 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -233,6 +233,8 @@ /// A reference to the object in the slot. Grabs or items, generally. var/datum/component/storage/master = null +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/close) + /atom/movable/screen/close/Initialize(mapload, new_master) . = ..() master = new_master @@ -440,6 +442,8 @@ /// A reference to the object in the slot. Grabs or items, generally. var/datum/component/storage/master = null +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/storage) + /atom/movable/screen/storage/Initialize(mapload, new_master) . = ..() master = new_master @@ -704,6 +708,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/splash) +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/splash) + /atom/movable/screen/splash/Initialize(mapload, client/C, visible, use_previous_title) . = ..() if(!istype(C)) @@ -745,6 +751,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/splash) /atom/movable/screen/component_button var/atom/movable/screen/parent +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/component_button) + /atom/movable/screen/component_button/Initialize(mapload, atom/movable/screen/parent) . = ..() src.parent = parent diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 0903d29dd8142..5712d240c4347 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -1,3 +1,26 @@ +/*! +## Debugging GC issues + +In order to debug `qdel()` failures, there are several tools available. +To enable these tools, define `TESTING` in [_compile_options.dm](https://github.com/tgstation/-tg-station/blob/master/code/_compile_options.dm). + +First is a verb called "Find References", which lists **every** refererence to an object in the world. This allows you to track down any indirect or obfuscated references that you might have missed. + +Complementing this is another verb, "qdel() then Find References". +This does exactly what you'd expect; it calls `qdel()` on the object and then it finds all references remaining. +This is great, because it means that `Destroy()` will have been called before it starts to find references, +so the only references you'll find will be the ones preventing the object from `qdel()`ing gracefully. + +If you have a datum or something you are not destroying directly (say via the singulo), +the next tool is `QDEL_HINT_FINDREFERENCE`. You can return this in `Destroy()` (where you would normally `return ..()`), +to print a list of references once it enters the GC queue. + +Finally is a verb, "Show qdel() Log", which shows the deletion log that the garbage subsystem keeps. This is helpful if you are having race conditions or need to review the order of deletions. + +Note that for any of these tools to work `TESTING` must be defined. +By using these methods of finding references, you can make your life far, far easier when dealing with `qdel()` failures. +*/ + SUBSYSTEM_DEF(garbage) name = "Garbage" priority = FIRE_PRIORITY_GARBAGE @@ -10,8 +33,8 @@ SUBSYSTEM_DEF(garbage) var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_CHECK_QUEUE, GC_DEL_QUEUE) // deciseconds to wait before moving something up in the queue to the next level //Stat tracking - var/delslasttick = 0 // number of del()'s we've done this tick - var/gcedlasttick = 0 // number of things that gc'ed last tick + var/delslasttick = 0 // number of del()'s we've done this tick + var/gcedlasttick = 0 // number of things that gc'ed last tick var/totaldels = 0 var/totalgcs = 0 @@ -21,7 +44,7 @@ SUBSYSTEM_DEF(garbage) var/list/pass_counts var/list/fail_counts - var/list/items = list() // Holds our qdel_item statistics datums + var/list/items = list() // Holds our qdel_item statistics datums //Queue var/list/queues @@ -33,19 +56,6 @@ SUBSYSTEM_DEF(garbage) #endif #endif -/datum/controller/subsystem/garbage/get_metrics() - . = ..() - var/list/cust = list() - // You can calculate TGCR in kibana - cust["total_harddels"] = totaldels - cust["total_softdels"] = totalgcs - var/i = 0 - for(var/list/L in queues) - i++ - cust["queue_[i]"] = length(L) - - .["custom"] = cust - /datum/controller/subsystem/garbage/PreInit() InitQueues() @@ -68,36 +78,42 @@ SUBSYSTEM_DEF(garbage) msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%" msg += " P:[pass_counts.Join(",")]" msg += "|F:[fail_counts.Join(",")]" - . = ..(msg) + return ..() /datum/controller/subsystem/garbage/Shutdown() //Adds the del() log to the qdel log file - var/list/dellog = list() + var/list/del_log = list() //sort by how long it's wasted hard deleting - sortTim(items, cmp=GLOBAL_PROC_REF(cmp_qdel_item_time), associative = TRUE) + sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) for(var/path in items) var/datum/qdel_item/I = items[path] - dellog += "Path: [path]" + var/list/entry = list() + del_log[path] = entry + if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG) - dellog += "\tSUSPENDED FOR LAG" + entry["SUSPENDED FOR LAG"] = TRUE if (I.failures) - dellog += "\tFailures: [I.failures]" - dellog += "\tqdel() Count: [I.qdels]" - dellog += "\tDestroy() Cost: [I.destroy_time]ms" + entry["Failures"] = I.failures + entry["qdel() Count"] = I.qdels + entry["Destroy() Cost (ms)"] = I.destroy_time + if (I.hard_deletes) - dellog += "\tTotal Hard Deletes [I.hard_deletes]" - dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms" - dellog += "\tHighest Time Spent Hard Deleting: [I.hard_delete_max]ms" + entry["Total Hard Deletes"] = I.hard_deletes + entry["Time Spend Hard Deleting (ms)"] = I.hard_delete_time + entry["Highest Time Spend Hard Deleting (ms)"] = I.hard_delete_max if (I.hard_deletes_over_threshold) - dellog += "\tHard Deletes Over Threshold: [I.hard_deletes_over_threshold]" + entry["Hard Deletes Over Threshold"] = I.hard_deletes_over_threshold if (I.slept_destroy) - dellog += "\tSleeps: [I.slept_destroy]" + entry["Total Sleeps"] = I.slept_destroy if (I.no_respect_force) - dellog += "\tIgnored force: [I.no_respect_force] times" + entry["Total Ignored Force"] = I.no_respect_force if (I.no_hint) - dellog += "\tNo hint: [I.no_hint] times" - log_qdel(dellog.Join("\n")) + entry["Total No Hint"] = I.no_hint + if(LAZYLEN(I.extra_details)) + entry["Deleted Metadata"] = I.extra_details + + log_qdel("", del_log) /datum/controller/subsystem/garbage/fire() //the fact that this resets its processing each fire (rather then resume where it left off) is intentional. @@ -129,6 +145,7 @@ SUBSYSTEM_DEF(garbage) pass_counts[i] = 0 fail_counts[i] = 0 + /datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_FILTER) if (level == GC_QUEUE_FILTER) delslasttick = 0 @@ -145,31 +162,37 @@ SUBSYSTEM_DEF(garbage) lastlevel = level - //We do this rather then for(var/refID in queue) because that sort of for loop copies the whole list. +// 1 from the hard reference in the queue, and 1 from the variable used before this +#define REFS_WE_EXPECT 2 + + //We do this rather then for(var/list/ref_info in queue) because that sort of for loop copies the whole list. //Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun. for (var/i in 1 to length(queue)) var/list/L = queue[i] - if (length(L) < 2) + if (length(L) < GC_QUEUE_ITEM_INDEX_COUNT) count++ if (MC_TICK_CHECK) return continue - var/GCd_at_time = L[1] - if(GCd_at_time > cut_off_time) + var/queued_at_time = L[GC_QUEUE_ITEM_QUEUE_TIME] + if(queued_at_time > cut_off_time) break // Everything else is newer, skip them count++ - var/refID = L[2] - var/datum/D - D = locate(refID) + var/datum/D = L[GC_QUEUE_ITEM_REF] - if (!D || D.gc_destroyed != GCd_at_time) // So if something else coincidentally gets the same ref, it's not deleted by mistake + // If that's all we've got, send er off +#if DM_VERSION >= 515 + if (refcount(D) == REFS_WE_EXPECT) +#else + if (!D || D.gc_destroyed != queued_at_time) +#endif ++gcedlasttick ++totalgcs pass_counts[level]++ #ifdef REFERENCE_TRACKING - reference_find_on_fail -= refID //It's deleted we don't care anymore. + reference_find_on_fail -= text_ref(D) //It's deleted we don't care anymore. #endif if (MC_TICK_CHECK) return @@ -185,7 +208,9 @@ SUBSYSTEM_DEF(garbage) switch (level) if (GC_QUEUE_CHECK) #ifdef REFERENCE_TRACKING - if(reference_find_on_fail[refID]) + // Decides how many refs to look for (potentially) + // Based off the remaining and the ones we can account for + if(reference_find_on_fail[text_ref(D)]) INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references)) ref_searching = TRUE #ifdef GC_FAILURE_HARD_LOOKUP @@ -193,12 +218,18 @@ SUBSYSTEM_DEF(garbage) INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references)) ref_searching = TRUE #endif - reference_find_on_fail -= refID + reference_find_on_fail -= text_ref(D) #endif var/type = D.type var/datum/qdel_item/I = items[type] - log_world("## TESTING: GC: -- [FAST_REF(D)] | [type] was unable to be GC'd --") + var/message = "## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --" + log_world(message) + + var/detail = D.dump_harddel_info() + if(detail) + LAZYADD(I.extra_details, detail) + #ifdef TESTING for(var/c in GLOB.admins) //Using testing() here would fill the logs with ADMIN_VV garbage var/client/admin = c @@ -233,38 +264,41 @@ SUBSYSTEM_DEF(garbage) queue.Cut(1,count+1) count = 0 +#undef REFS_WE_EXPECT + /datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_FILTER) if (isnull(D)) return if (level > GC_QUEUE_COUNT) HardDelete(D) return - var/gctime = world.time - var/refid = FAST_REF(D) + var/queue_time = world.time - D.gc_destroyed = gctime - var/list/queue = queues[level] + if (D.gc_destroyed <= 0) + D.gc_destroyed = queue_time - queue[++queue.len] = list(gctime, refid) // not += for byond reasons + var/list/queue = queues[level] + queue[++queue.len] = list(queue_time, D, D.gc_destroyed) // not += for byond reasons //this is mainly to separate things profile wise. /datum/controller/subsystem/garbage/proc/HardDelete(datum/D) ++delslasttick ++totaldels var/type = D.type - var/refID = FAST_REF(D) + var/refID = text_ref(D) + var/datum/qdel_item/type_info = items[type] + var/detail = D.dump_harddel_info() + if(detail) + LAZYADD(type_info.extra_details, detail) var/tick_usage = TICK_USAGE del(D) - tick_usage = TICK_USAGE_TO_MS(tick_usage) - var/datum/qdel_item/I = items[type] - - I.hard_deletes++ - I.hard_delete_time += tick_usage - if (tick_usage > I.hard_delete_max) - I.hard_delete_max = tick_usage + type_info.hard_deletes++ + type_info.hard_delete_time += tick_usage + if (tick_usage > type_info.hard_delete_max) + type_info.hard_delete_max = tick_usage if (tick_usage > highest_del_ms) highest_del_ms = tick_usage highest_del_type_string = "[type]" @@ -273,16 +307,16 @@ SUBSYSTEM_DEF(garbage) if (time > 0.1 SECONDS) postpone(time) - var/threshold = CONFIG_GET(number/hard_deletes_overrun_threshold) if (threshold && (time > threshold SECONDS)) - if (!(I.qdel_flags & QDEL_ITEM_ADMINS_WARNED)) + if (!(type_info.qdel_flags & QDEL_ITEM_ADMINS_WARNED)) log_game("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete)") - I.qdel_flags |= QDEL_ITEM_ADMINS_WARNED - I.hard_deletes_over_threshold++ + message_admins("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete).") + type_info.qdel_flags |= QDEL_ITEM_ADMINS_WARNED + type_info.hard_deletes_over_threshold++ var/overrun_limit = CONFIG_GET(number/hard_deletes_overrun_limit) - if (overrun_limit && I.hard_deletes_over_threshold >= overrun_limit) - I.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG + if (overrun_limit && type_info.hard_deletes_over_threshold >= overrun_limit) + type_info.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG /datum/controller/subsystem/garbage/Recover() InitQueues() //We first need to create the queues before recovering data @@ -304,78 +338,85 @@ SUBSYSTEM_DEF(garbage) var/no_hint = 0 //!Number of times it's not even bother to give a qdel hint var/slept_destroy = 0 //!Number of times it's slept in its destroy var/qdel_flags = 0 //!Flags related to this type's trip thru qdel. + var/list/extra_details //!Lazylist of string metadata about the deleted objects /datum/qdel_item/New(mytype) name = "[mytype]" +/// Should be treated as a replacement for the 'del' keyword. +/// +/// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. +/proc/qdel(datum/to_delete, force = FALSE) + if(!istype(to_delete)) + del(to_delete) + return + + var/datum/qdel_item/trash = SSgarbage.items[to_delete.type] + if (isnull(trash)) + trash = SSgarbage.items[to_delete.type] = new /datum/qdel_item(to_delete.type) + trash.qdels++ -// Should be treated as a replacement for the 'del' keyword. -// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. -/proc/qdel(datum/D, force=FALSE, ...) - if(!istype(D)) - del(D) + if(!isnull(to_delete.gc_destroyed)) + if(to_delete.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + CRASH("[to_delete.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") return - var/datum/qdel_item/I = SSgarbage.items[D.type] - if (!I) - I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type) - I.qdels++ + if (SEND_SIGNAL(to_delete, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted + return - if(isnull(D.gc_destroyed)) - if (SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted - return - D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED - var/start_time = world.time - var/start_tick = world.tick_usage - SEND_SIGNAL(D, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy - var/hint = D.Destroy(arglist(args.Copy(2))) // Let our friend know they're about to get fucked up. - if(world.time != start_time) - I.slept_destroy++ - else - I.destroy_time += TICK_USAGE_TO_MS(start_tick) - if(!D) + to_delete.gc_destroyed = GC_CURRENTLY_BEING_QDELETED + var/start_time = world.time + var/start_tick = world.tick_usage + SEND_SIGNAL(to_delete, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy + var/hint = to_delete.Destroy(force) // Let our friend know they're about to get fucked up. + + if(world.time != start_time) + trash.slept_destroy++ + else + trash.destroy_time += TICK_USAGE_TO_MS(start_tick) + + if(isnull(to_delete)) + return + + switch(hint) + if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. + SSgarbage.Queue(to_delete) + if (QDEL_HINT_IWILLGC) + to_delete.gc_destroyed = world.time return - switch(hint) - if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. - SSgarbage.Queue(D) - if (QDEL_HINT_IWILLGC) - D.gc_destroyed = world.time + if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory. + if(!force) + to_delete.gc_destroyed = null //clear the gc variable (important!) return - if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory. - if(!force) - D.gc_destroyed = null //clear the gc variable (important!) - return - // Returning LETMELIVE after being told to force destroy - // indicates the objects Destroy() does not respect force - #ifdef TESTING - if(!I.no_respect_force) - testing("WARNING: [D.type] has been force deleted, but is \ - returning an immortal QDEL_HINT, indicating it does \ - not respect the force flag for qdel(). It has been \ - placed in the queue, further instances of this type \ - will also be queued.") - #endif - I.no_respect_force++ + // Returning LETMELIVE after being told to force destroy + // indicates the objects Destroy() does not respect force + #ifdef TESTING + if(!trash.no_respect_force) + testing("WARNING: [to_delete.type] has been force deleted, but is \ + returning an immortal QDEL_HINT, indicating it does \ + not respect the force flag for qdel(). It has been \ + placed in the queue, further instances of this type \ + will also be queued.") + #endif + trash.no_respect_force++ - SSgarbage.Queue(D) - if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete - SSgarbage.Queue(D, GC_QUEUE_HARDDELETE) - if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. - SSgarbage.HardDelete(D) - #ifdef REFERENCE_TRACKING - if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion. - SSgarbage.Queue(D) - D.find_references() //This breaks ci. Consider it insurance against somehow pring reftracking on accident - if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object. - SSgarbage.Queue(D) - SSgarbage.reference_find_on_fail[FAST_REF(D)] = TRUE + SSgarbage.Queue(to_delete) + if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete + SSgarbage.Queue(to_delete, GC_QUEUE_HARDDELETE) + if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. + SSgarbage.HardDelete(to_delete) + #ifdef REFERENCE_TRACKING + if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion. + SSgarbage.Queue(to_delete) + INVOKE_ASYNC(to_delete, TYPE_PROC_REF(/datum, find_references)) + if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object. + SSgarbage.Queue(to_delete) + SSgarbage.reference_find_on_fail[text_ref(to_delete)] = TRUE + #endif + else + #ifdef TESTING + if(!trash.no_hint) + testing("WARNING: [to_delete.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") #endif - else - #ifdef TESTING - if(!I.no_hint) - testing("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") - #endif - I.no_hint++ - SSgarbage.Queue(D) - else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") + trash.no_hint++ + SSgarbage.Queue(to_delete) diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index b1c5d00fffd2d..33a6eec70573d 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -94,6 +94,8 @@ to_chat(src, "You are absolutely loyal to your friend, no matter what.") to_chat(src, "You cannot directly influence the world around you, but you can see what [owner] cannot.") +CREATION_TEST_IGNORE_SUBTYPES(/mob/camera/imaginary_friend) + /mob/camera/imaginary_friend/Initialize(mapload, _trauma) . = ..() diff --git a/code/datums/brain_damage/mrat.dm b/code/datums/brain_damage/mrat.dm index 964bf75ce15ef..be0925b5f3043 100644 --- a/code/datums/brain_damage/mrat.dm +++ b/code/datums/brain_damage/mrat.dm @@ -114,6 +114,8 @@ to_chat(src, "Your job is to answer [owner]'s question(s) and you are given this form to assist in that.") to_chat(src, "Don't be stupid with this or you will face the consequences.") +CREATION_TEST_IGNORE_SUBTYPES(/mob/camera/imaginary_friend/mrat) + /mob/camera/imaginary_friend/mrat/Initialize(mapload, _trauma) . = ..() costume = new diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index ba9da36e06630..3ba80201c2fa6 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -127,6 +127,8 @@ var/mob/living/carbon/body var/datum/brain_trauma/severe/split_personality/trauma +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/split_personality) + /mob/living/split_personality/Initialize(mapload, _trauma) if(iscarbon(loc)) body = loc diff --git a/code/datums/components/udder.dm b/code/datums/components/udder.dm index abfea6a842241..cbb1f5ea64ca9 100644 --- a/code/datums/components/udder.dm +++ b/code/datums/components/udder.dm @@ -71,6 +71,8 @@ ///optional proc to callback to when the udder generates milk var/datum/callback/on_generate_callback +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/udder) + /obj/item/udder/Initialize(mapload, udder_mob, on_generate_callback, reagent_produced_typepath = /datum/reagent/consumable/milk) src.udder_mob = udder_mob src.on_generate_callback = on_generate_callback diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 5de92c7d2911a..270c870bedef5 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -64,6 +64,10 @@ #endif #endif + // If we have called dump_harddel_info already. Used to avoid duped calls (since we call it immediately in some cases on failure to process) + // Create and destroy is weird and I wanna cover my bases + var/harddel_deets_dumped = FALSE + #ifdef DATUMVAR_DEBUGGING_MODE var/list/cached_vars #endif @@ -267,3 +271,16 @@ return SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index)) TIMER_COOLDOWN_END(source, index) + +/// Return text from this proc to provide extra context to hard deletes that happen to it +/// Optional, you should use this for cases where replication is difficult and extra context is required +/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry) +/datum/proc/dump_harddel_info() + return + +///images are pretty generic, this should help a bit with tracking harddels related to them +/image/dump_harddel_info() + if(harddel_deets_dumped) + return + harddel_deets_dumped = TRUE + return "Image icon: [icon] - icon_state: [icon_state] [loc ? "loc: [loc] ([loc.x],[loc.y],[loc.z])" : ""]" diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 44c73d4263386..2f88159ad3296 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -222,6 +222,8 @@ var/turf/destination var/has_hud_icon = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/teleportation_wake) + /obj/effect/temp_visual/teleportation_wake/Initialize(mapload, turf/destination) // Replace any portals on the current turf for (var/obj/effect/temp_visual/teleportation_wake/conflicting_portal in loc) diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm index e6b094acc077b..2361303ae9416 100644 --- a/code/datums/map_config.dm +++ b/code/datums/map_config.dm @@ -40,6 +40,9 @@ /// Is night lighting allowed to occur on this station? var/allow_night_lighting = TRUE + /// List of unit tests that are skipped when running this map + var/list/skipped_tests + //====== // Starlight Settings //====== @@ -181,6 +184,16 @@ planet_mass = text2num(json["planet_mass"]) || planet_mass planet_radius = text2num(json["planet_radius"]) || planet_radius +#ifdef UNIT_TESTS + // Check for unit tests to skip, no reason to check these if we're not running tests + for(var/path_as_text in json["ignored_unit_tests"]) + var/path_real = text2path(path_as_text) + if(!ispath(path_real, /datum/unit_test)) + stack_trace("Invalid path in mapping config for ignored unit tests: \[[path_as_text]\]") + continue + LAZYADD(skipped_tests, path_real) +#endif + defaulted = FALSE return TRUE #undef CHECK_EXISTS diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index e9d1f11857ccd..ecc24b4e64555 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -135,6 +135,13 @@ QDEL_IN(src, PROGRESSBAR_ANIMATION_TIME) +///Progress bars are very generic, and what hangs a ref to them depends heavily on the context in which they're used +///So let's make hunting harddels easier yeah? +/datum/progressbar/dump_harddel_info() + if(harddel_deets_dumped) + return + harddel_deets_dumped = TRUE + return "Location type: [bar_loc.type], User type: [user.type]" #undef PROGRESSBAR_ANIMATION_TIME #undef PROGRESSBAR_HEIGHT diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm index 2de64b6e62c5b..700632b664df4 100644 --- a/code/game/area/Space_Station_13_areas.dm +++ b/code/game/area/Space_Station_13_areas.dm @@ -1208,7 +1208,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/security/brig/medbay name = "Brig Bay" - + /area/security/brig/aft name = "Brig Aft" @@ -1463,7 +1463,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station sound_environment = SOUND_AREA_STANDARD_STATION airlock_hack_difficulty = AIRLOCK_WIRE_SECURITY_ADVANCED color_correction = /datum/client_colour/area_color/cold_ish - + /area/science/aft name = "Science Aft" @@ -1498,6 +1498,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station area_flags = BLOBS_ALLOWED | UNIQUE_AREA icon_state = "tox_test" lights_always_start_on = TRUE + always_unpowered = TRUE /area/science/mixing name = "Toxins Mixing Lab" diff --git a/code/game/area/area_color_correction.dm b/code/game/area/area_color_correction.dm index 63dd18fbda70d..2e76ecae4fae6 100644 --- a/code/game/area/area_color_correction.dm +++ b/code/game/area/area_color_correction.dm @@ -4,8 +4,7 @@ colour vs color */ -/mob - var/current_correction +/mob/var/current_correction /datum/client_colour/area_color colour = list(rgb(255, 0, 0), rgb(0, 255, 0), rgb(0, 0, 255)) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 0deec87f6c61d..94f466b2cdd98 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -212,6 +212,8 @@ * * /turf/Initialize * * /turf/open/space/Initialize */ +CREATION_TEST_IGNORE_SUBTYPES(/atom/proc) + /atom/proc/Initialize(mapload, ...) if(flags_1 & INITIALIZED_1) stack_trace("Warning: [src]([type]) initialized multiple times!") diff --git a/code/game/gamemodes/gangs/gang_tags.dm b/code/game/gamemodes/gangs/gang_tags.dm index f0e2f7932cf19..5c2ab12bfb768 100644 --- a/code/game/gamemodes/gangs/gang_tags.dm +++ b/code/game/gamemodes/gangs/gang_tags.dm @@ -7,6 +7,8 @@ gender = NEUTER var/datum/team/gang/gang +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/gang) + /obj/effect/decal/gang/Initialize(mapload, datum/team/gang/G, e_name = "gang tag", rotation = 0, mob/user) if(!G) qdel(src) diff --git a/code/game/gamemodes/gangs/implant_gang.dm b/code/game/gamemodes/gangs/implant_gang.dm index 25f32cf845208..0cab2aa4b9baf 100644 --- a/code/game/gamemodes/gangs/implant_gang.dm +++ b/code/game/gamemodes/gangs/implant_gang.dm @@ -4,6 +4,8 @@ activated = 0 var/datum/team/gang/gang +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/implant/gang) + /obj/item/implant/gang/Initialize(mapload, loc, setgang) ..() gang = setgang @@ -39,6 +41,8 @@ /obj/item/implanter/gang name = "implanter (gang)" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/implanter/gang) + /obj/item/implanter/gang/Initialize(mapload, loc, gang) if(!gang) qdel(src) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 9efcba6ab33bf..3c4bfee04b5c7 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -141,6 +141,8 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/lifetime = DEFAULT_METEOR_LIFETIME +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/meteor) + /obj/effect/meteor/Initialize(mapload, target) . = ..() z_original = z @@ -436,6 +438,8 @@ GLOBAL_LIST_INIT(meteorsSPOOKY, list(/obj/effect/meteor/pumpkin)) var/prefalltime = 8 SECONDS layer = METEOR_LAYER +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/falling_meteor) + /obj/effect/falling_meteor/Initialize(mapload, meteor_type) . = ..() if(!meteor_type) diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 187bcdbb04acf..4990b81131ab8 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -2,8 +2,8 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE) -/mob - var/datum/hSB/sandbox = null +/mob/var/datum/hSB/sandbox = null + /mob/proc/CanBuild() sandbox = new/datum/hSB sandbox.owner = src.ckey diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 79f53e53dc643..e2f637ce90b3c 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -19,6 +19,8 @@ /obj/machinery/button/indestructible resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/button) + /obj/machinery/button/Initialize(mapload, ndir = 0, built = 0) . = ..() if(built) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 829dad51359f0..ac6e4b1bcbfe2 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -72,6 +72,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) light_range = 10 start_active = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/camera) + /obj/machinery/camera/Initialize(mapload, obj/structure/camera_assembly/CA) . = ..() for(var/i in network) diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index 5f5168f82169b..74e05c0b6e29c 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -57,6 +57,8 @@ if(STATE_FINISHED) . += "You shouldn't be seeing this, tell a coder!" +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/camera_assembly) + /obj/structure/camera_assembly/Initialize(mapload, ndir, building) . = ..() if(building) diff --git a/code/game/machinery/computer/arena.dm b/code/game/machinery/computer/arena.dm index 86312b24e4323..d13be687a01b4 100644 --- a/code/game/machinery/computer/arena.dm +++ b/code/game/machinery/computer/arena.dm @@ -62,6 +62,8 @@ var/start_sound = 'sound/items/airhorn2.ogg' var/start_sound_volume = 50 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/arena) + /obj/machinery/computer/arena/Initialize(mapload, obj/item/circuitboard/C) . = ..() LoadDefaultArenas() diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 3b6e335252130..c44799a9d85ae 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -16,6 +16,8 @@ light_color = LIGHT_COLOR_BLUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/crew) + /obj/machinery/computer/crew/Initialize(mapload, obj/item/circuitboard/C) . = ..() AddComponent(/datum/component/usb_port, list( diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 827960f17fb20..5cbb521bd69a8 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -19,6 +19,8 @@ var/order = 1 // -1 = Descending - 1 = Ascending light_color = LIGHT_COLOR_RED +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/secure_data) + /obj/machinery/computer/secure_data/Initialize(mapload, obj/item/circuitboard/C) . = ..() AddComponent(/datum/component/usb_port, list( diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index c531f9d1fe7e6..197d720b3e3d6 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -25,6 +25,8 @@ var/rods = 2 var/cable = 1 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/door/window) + /obj/machinery/door/window/Initialize(mapload, set_dir, unres_sides) . = ..() if(set_dir) diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 74f93e38794c2..05c5d4a676516 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -96,6 +96,8 @@ /obj/item/paper/record_printout name = "paper - Log Recording" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/paper/record_printout) + /obj/item/paper/record_printout/Initialize(mapload, datum/data/tachyon_record/record) . = ..() diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index d429b35023c00..f07abb9357745 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -40,6 +40,8 @@ var/area/myarea = null var/locked = FALSE //Are we locked? +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/firealarm) + /obj/machinery/firealarm/Initialize(mapload, dir, building) . = ..() if (!req_access) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 50d8a0fc21d7f..39cd58f65a9ad 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -31,6 +31,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/flasher, 26) light_range = FLASH_LIGHT_RANGE light_on = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/flasher) + /obj/machinery/flasher/Initialize(mapload, ndir = 0, built = 0) . = ..() // ..() is EXTREMELY IMPORTANT, never forget to add it if(!built) diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index 543213f2a6616..04b57fbb2b059 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -230,6 +230,8 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/launchpad) var/closed = TRUE var/obj/item/storage/briefcase/launchpad/briefcase +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/launchpad/briefcase) + /obj/machinery/launchpad/briefcase/Initialize(mapload, briefcase) . = ..() if(!briefcase) @@ -334,6 +336,8 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/launchpad) //A weakref to our linked pad var/datum/weakref/pad +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/launchpad_remote) + /obj/item/launchpad_remote/Initialize(mapload, pad) //remote spawns linked to the briefcase pad . = ..() src.pad = WEAKREF(pad) diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index d6c1e98d6d477..04d4207fc0a84 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -59,6 +59,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/newscaster) + /obj/machinery/newscaster/Initialize(mapload, ndir, building) . = ..() GLOB.allCasters += src diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 66e32708181a7..dc911ee7c38f9 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -41,6 +41,8 @@ Buildable meters //Flipping handled manually due to custom handling for trinary pipes AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/pipe) + /obj/item/pipe/Initialize(mapload, _pipe_type, _dir, obj/machinery/atmospherics/make_from) if(make_from) make_from_existing(make_from) diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index e6cee442b0401..64ff5e4c33e43 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -874,6 +874,8 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/porta_turret) /// List of all linked turrets var/list/turrets = list() +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/turretid) + /obj/machinery/turretid/Initialize(mapload, ndir = 0, built = 0) . = ..() if(built) diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 91609daabb065..bd6b0d431ffc4 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -416,6 +416,8 @@ var/obj/machinery/shieldwallgen/gen_primary var/obj/machinery/shieldwallgen/gen_secondary +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/shieldwall) + /obj/machinery/shieldwall/Initialize(mapload, obj/machinery/shieldwallgen/first_gen, obj/machinery/shieldwallgen/second_gen) . = ..() gen_primary = first_gen diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 3c0e11ff55ac8..200fe3e300aad 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -39,6 +39,8 @@ var/header_text_color = "#2CF" //makes it go on the wall when built +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/status_display) + /obj/machinery/status_display/Initialize(mapload, ndir, building) . = ..() update_appearance() @@ -216,6 +218,8 @@ 5, 5, 5, 5, 4, 5, 4, 6, 4, 4, 4, 3, 2, 3, 4, ) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/overlay/status_display_text) + /obj/effect/overlay/status_display_text/Initialize(mapload, yoffset, line, text_color, header_text_color) . = ..() diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index 66a5f419eb3ae..a63b62fb27c2e 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -282,6 +282,8 @@ /obj/item/paper/monitorkey name = "monitor decryption key" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/paper/monitorkey) + /obj/item/paper/monitorkey/Initialize(mapload, obj/machinery/telecomms/message_server/server) ..() if (server) diff --git a/code/game/objects/effects/alien_acid.dm b/code/game/objects/effects/alien_acid.dm index a63780cf2ba35..ebdfc88e53f6e 100644 --- a/code/game/objects/effects/alien_acid.dm +++ b/code/game/objects/effects/alien_acid.dm @@ -11,6 +11,8 @@ var/turf/target +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/acid) + /obj/effect/acid/Initialize(mapload, acid_pwr, acid_amt) . = ..() diff --git a/code/game/objects/effects/anomalies/_anomalies.dm b/code/game/objects/effects/anomalies/_anomalies.dm index de035db4da6d9..5a094416c945e 100644 --- a/code/game/objects/effects/anomalies/_anomalies.dm +++ b/code/game/objects/effects/anomalies/_anomalies.dm @@ -25,6 +25,8 @@ ///How many harvested pierced realities do we spawn on destruction var/max_spawned_faked = 2 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/anomaly) + /obj/effect/anomaly/Initialize(mapload, new_lifespan, spawned_fake_harvested) . = ..() diff --git a/code/game/objects/effects/anomalies/anomaly_flux.dm b/code/game/objects/effects/anomalies/anomaly_flux.dm index 5f4fdc9739807..20acff19cbe19 100644 --- a/code/game/objects/effects/anomalies/anomaly_flux.dm +++ b/code/game/objects/effects/anomalies/anomaly_flux.dm @@ -6,6 +6,8 @@ var/shockdamage = 20 var/explosive = ANOMALY_FLUX_EXPLOSIVE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/anomaly/flux) + /obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, drops_core = TRUE, explosive = ANOMALY_FLUX_EXPLOSIVE) . = ..() src.explosive = explosive diff --git a/code/game/objects/effects/anomalies/anomaly_gravity.dm b/code/game/objects/effects/anomalies/anomaly_gravity.dm index bc06bc3fba8ba..fdda56b57be9a 100644 --- a/code/game/objects/effects/anomalies/anomaly_gravity.dm +++ b/code/game/objects/effects/anomalies/anomaly_gravity.dm @@ -14,6 +14,8 @@ ///Warp effect holder for displacement filter to "pulse" the anomaly var/atom/movable/warp_effect/warp +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/anomaly/grav) + /obj/effect/anomaly/grav/Initialize(mapload, new_lifespan, drops_core) . = ..() var/static/list/loc_connections = list( @@ -76,6 +78,8 @@ /obj/effect/anomaly/grav/high var/grav_field +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/anomaly/grav/high) + /obj/effect/anomaly/grav/high/Initialize(mapload, new_lifespan) . = ..() setup_grav_field() diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm index 07c0d3f0c5d3b..4f75ae2673003 100644 --- a/code/game/objects/effects/contraband.dm +++ b/code/game/objects/effects/contraband.dm @@ -12,6 +12,8 @@ var/poster_type var/obj/structure/sign/poster/poster_structure +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/poster) + /obj/item/poster/Initialize(mapload, obj/structure/sign/poster/new_poster_structure) . = ..() poster_structure = new_poster_structure diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 5d1e841ae5026..59d70bfce96d8 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -9,6 +9,8 @@ ///The type of cleaning required to clean the decal, CLEAN_TYPE_LIGHT_DECAL can be cleaned with mops and soap, CLEAN_TYPE_HARD_DECAL can be cleaned by soap, see __DEFINES/cleaning.dm for the others var/clean_type = CLEAN_TYPE_LIGHT_DECAL +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/cleanable) + /obj/effect/decal/cleanable/Initialize(mapload, list/datum/disease/diseases) . = ..() if (random_icon_states && (icon_state == initial(icon_state)) && length(random_icon_states) > 0) diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 5c37486b41ade..f92b63ee31a85 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -61,6 +61,8 @@ icon_state = "floor1-old" var/list/datum/disease/diseases = list() +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/cleanable/blood/old) + /obj/effect/decal/cleanable/blood/old/Initialize(mapload, list/datum/disease/diseases) add_blood_DNA(list("Non-human DNA" = random_blood_type())) // Needs to happen before ..() . = ..() @@ -112,6 +114,8 @@ ///Information about the diseases our streaking spawns var/list/streak_diseases +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/cleanable/blood/gibs) + /obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases) . = ..() reagents.add_reagent(/datum/reagent/liquidgibs, 5) @@ -205,6 +209,8 @@ drydesc = "Space Jesus, why didn't anyone clean this up? They smell terrible." var/list/datum/disease/diseases = list() +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/cleanable/blood/gibs/old) + /obj/effect/decal/cleanable/blood/gibs/old/Initialize(mapload, list/datum/disease/diseases) . = ..() setDir(pick(1, 2, 4, 8)) diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index c5baa5696e985..57c65ecebcbc8 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -142,6 +142,8 @@ desc = "You try not to look at the chunks, and fail." var/list/datum/disease/diseases = list() +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/cleanable/vomit/old) + /obj/effect/decal/cleanable/vomit/old/Initialize(mapload, list/datum/disease/diseases) . = ..() icon_state += "-old" diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index 5e7e025fbcbd9..4fe8fb4ef0a0c 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -10,6 +10,8 @@ var/rotation = 0 var/paint_colour = "#FFFFFF" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/decal/cleanable/crayon) + /obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null) . = ..() if(e_name) diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm index bf7ae1fb93d37..fc8577bf8a700 100644 --- a/code/game/objects/effects/forcefields.dm +++ b/code/game/objects/effects/forcefields.dm @@ -9,6 +9,8 @@ z_flags = Z_BLOCK_IN_DOWN | Z_BLOCK_IN_UP var/timeleft = 300 //Set to 0 for permanent forcefields (ugh) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/forcefield) + /obj/effect/forcefield/Initialize(mapload, ntimeleft) . = ..() if(isnum_safe(ntimeleft)) @@ -39,6 +41,8 @@ desc = "You're gonna be here awhile." timeleft = 600 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/forcefield/mime) + /obj/effect/forcefield/mime/Initialize(mapload, ntimeleft) . = ..() SSvis_overlays.add_obj_alpha(src, 'icons/turf/walls/snow_wall.dmi', "snow_wall-0") diff --git a/code/game/objects/effects/icons.dm b/code/game/objects/effects/icons.dm index d202ac5ff8fb6..01c8248303d27 100644 --- a/code/game/objects/effects/icons.dm +++ b/code/game/objects/effects/icons.dm @@ -4,10 +4,14 @@ name = "" mouse_opacity = MOUSE_OPACITY_TRANSPARENT +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/icon) + /obj/effect/icon/Initialize(mapload, icon/render_source) . = ..() overlays = list(render_source) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/icon/temp) + /obj/effect/icon/temp/Initialize(mapload, icon/render_source, duration) . = ..() addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), src), duration, TIMER_STOPPABLE | TIMER_CLIENT_TIME) diff --git a/code/game/objects/effects/info.dm b/code/game/objects/effects/info.dm index 23f341c8a3c78..2f2feee80bb2d 100644 --- a/code/game/objects/effects/info.dm +++ b/code/game/objects/effects/info.dm @@ -7,6 +7,8 @@ /// What should the info button display when clicked? var/info_text +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/abstract/info) + /obj/effect/abstract/info/Initialize(mapload, info_text) . = ..() diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 6031d3bab396d..ccdb8b598f528 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -488,6 +488,8 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player) /obj/effect/landmark/ruin var/datum/map_template/ruin/ruin_template +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/landmark/ruin) + /obj/effect/landmark/ruin/Initialize(mapload, my_ruin_template) . = ..() name = "ruin_[GLOB.ruin_landmarks.len + 1]" diff --git a/code/game/objects/effects/lighting.dm b/code/game/objects/effects/lighting.dm index a0838f6e840fa..4f054ba2044aa 100644 --- a/code/game/objects/effects/lighting.dm +++ b/code/game/objects/effects/lighting.dm @@ -14,6 +14,8 @@ //blocks_emissive = EMISSIVE_BLOCK_NONE mouse_opacity = MOUSE_OPACITY_TRANSPARENT +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/dummy/lighting_obj) + /obj/effect/dummy/lighting_obj/Initialize(mapload, range, power, color, duration) . = ..() if(!isnull(range)) @@ -28,6 +30,8 @@ /obj/effect/dummy/lighting_obj/moblight name = "mob" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/dummy/lighting_obj/moblight) + /obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, range, power, color, duration) . = ..() if(!ismob(loc)) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 472cb998b7797..2a9ac99c7d876 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -86,6 +86,8 @@ if(Adjacent(user)) teleport(user) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/portal) + /obj/effect/portal/Initialize(mapload, _creator, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override) . = ..() GLOB.portals += src @@ -208,6 +210,8 @@ var/id // var edit or set id in map editor hardlinked = FALSE // dont qdel my portal nerd +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/portal/permanent) + /obj/effect/portal/permanent/Initialize(mapload, _creator, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override) . = ..() set_linked() diff --git a/code/game/objects/effects/proximity.dm b/code/game/objects/effects/proximity.dm index f7502f744b08d..dea082420fd05 100644 --- a/code/game/objects/effects/proximity.dm +++ b/code/game/objects/effects/proximity.dm @@ -100,6 +100,8 @@ anchored = TRUE var/datum/proximity_monitor/monitor +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/abstract/proximity_checker) + /obj/effect/abstract/proximity_checker/Initialize(mapload, datum/proximity_monitor/_monitor) . = ..() if(_monitor) diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index 852b86dc7a865..6df5ff54ff243 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -9,6 +9,8 @@ var/list/gibamounts = list() //amount to spawn for each gib decal type we'll spawn. var/list/gibdirections = list() //of lists of possible directions to spread each gib decal type towards. +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/gibspawner) + /obj/effect/gibspawner/Initialize(mapload, mob/living/source_mob, list/datum/disease/diseases) . = ..() diff --git a/code/game/objects/effects/spawners/roomspawner.dm b/code/game/objects/effects/spawners/roomspawner.dm index 2444e6cf3aca1..547d0e030c267 100644 --- a/code/game/objects/effects/spawners/roomspawner.dm +++ b/code/game/objects/effects/spawners/roomspawner.dm @@ -1,41 +1,56 @@ //random room spawner. takes random rooms from their appropriate map file and places them. the room will spawn with the spawner in the bottom left corner /obj/effect/spawner/room - name = "random room spawner" - icon = 'icons/effects/landmarks_static.dmi' - icon_state = "random_room" - dir = NORTH - var/room_width = 0 - var/room_height = 0 - ///List of room IDs we want - var/list/rooms = list() + name = "random room spawner" + icon = 'icons/effects/landmarks_static.dmi' + icon_state = "random_room" + dir = NORTH + var/room_width = 0 + var/room_height = 0 + ///List of room IDs we want + var/list/rooms = list() /obj/effect/spawner/room/New(loc, ...) - . = ..() - if(!isnull(SSmapping.random_room_spawners)) - SSmapping.random_room_spawners += src + . = ..() + if(!isnull(SSmapping.random_room_spawners)) + SSmapping.random_room_spawners += src /obj/effect/spawner/room/Initialize(mapload) - . = ..() - if(!length(SSmapping.random_room_templates)) - message_admins("Room spawner created with no templates available. This shouldn't happen.") - return INITIALIZE_HINT_QDEL - var/list/possibletemplates = list() - var/datum/map_template/random_room/candidate - shuffle_inplace(SSmapping.random_room_templates) - for(var/ID in SSmapping.random_room_templates) - candidate = SSmapping.random_room_templates[ID] - if((!rooms.len && candidate.spawned) || (!rooms.len && (room_height != candidate.template_height || room_width != candidate.template_width)) || (rooms.len && !(candidate.room_id in rooms))) - candidate = null - continue - possibletemplates[candidate] = candidate.weight - if(possibletemplates.len) - var/datum/map_template/random_room/template = pick_weight(possibletemplates) - template.stock -- - template.weight = (template.weight / 2) - if(template.stock <= 0) - template.spawned = TRUE - template.load(get_turf(src), centered = template.centerspawner) + . = ..() + if(!length(SSmapping.random_room_templates)) + message_admins("Room spawner created with no templates available. This shouldn't happen.") + return INITIALIZE_HINT_QDEL + var/list/possibletemplates = list() + var/datum/map_template/random_room/candidate + shuffle_inplace(SSmapping.random_room_templates) + for(var/ID in SSmapping.random_room_templates) + candidate = SSmapping.random_room_templates[ID] + if((!rooms.len && candidate.spawned) || (!rooms.len && (room_height != candidate.template_height || room_width != candidate.template_width)) || (rooms.len && !(candidate.room_id in rooms))) + candidate = null + continue + possibletemplates[candidate] = candidate.weight + if(possibletemplates.len) + var/datum/map_template/random_room/template = pick_weight(possibletemplates) + template.stock -- + template.weight = (template.weight / 2) + if(template.stock <= 0) + template.spawned = TRUE + var/datum/async_map_generator/map_place/generator = template.load(get_turf(src), centered = template.centerspawner) + generator.on_completion(CALLBACK(src, PROC_REF(after_place))) + +/obj/effect/spawner/room/proc/after_place(datum/async_map_generator/map_place/generator, turf/T, init_atmos, datum/parsed_map/parsed, finalize = TRUE, ...) + // Scan through the room and remove any wall fixtures that were not placed correctly + for (var/x in T.x to T.x + room_width) + for (var/y in T.y to T.y + room_height) + var/turf/current = locate(x, y, T.z) + for (var/obj/placed_object in current) + // Temporary hacky check to see if we contain a directional mapping helper + // I know its a normal variable, but this is explicitly accessed through reflection + if (!initial(placed_object._reflection_is_directional)) + continue + // Check to see if we correctly placed ourselves on a wall + if (!isclosedturf(get_step(placed_object, placed_object.dir))) + qdel(placed_object) /obj/effect/spawner/room/special/tenxfive_terrestrial name = "10x5 terrestrial room" diff --git a/code/game/objects/effects/temporary_visuals/clockwork.dm b/code/game/objects/effects/temporary_visuals/clockwork.dm index 65e700fd6d128..ffbac1b47be2e 100644 --- a/code/game/objects/effects/temporary_visuals/clockwork.dm +++ b/code/game/objects/effects/temporary_visuals/clockwork.dm @@ -109,6 +109,8 @@ pixel_x = -16 duration = 30 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/ratvar/prolonging_prism) + /obj/effect/temp_visual/ratvar/prolonging_prism/Initialize(mapload, set_appearance) . = ..() if(set_appearance) @@ -159,6 +161,8 @@ icon_state = "smoke" duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/steam) + /obj/effect/temp_visual/steam/Initialize(mapload, steam_direction) . = ..() setDir(steam_direction) diff --git a/code/game/objects/effects/temporary_visuals/cult.dm b/code/game/objects/effects/temporary_visuals/cult.dm index 473405deded44..3461069224a61 100644 --- a/code/game/objects/effects/temporary_visuals/cult.dm +++ b/code/game/objects/effects/temporary_visuals/cult.dm @@ -72,6 +72,8 @@ alpha = 0 var/turnedness = 179 //179 turns counterclockwise, 181 turns clockwise +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/cult/rune_spawn) + /obj/effect/temp_visual/cult/rune_spawn/Initialize(mapload, set_duration, set_color) if(isnum_safe(set_duration)) duration = set_duration diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 94fbdfa71bb3d..1206d43824256 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -6,6 +6,8 @@ layer = BELOW_MOB_LAYER var/splatter_type = "splatter" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dir_setting/bloodsplatter) + /obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir) if(set_dir in GLOB.diagonals) icon_state = "[splatter_type][pick(1, 2, 6)]" @@ -129,11 +131,15 @@ /obj/effect/temp_visual/dir_setting/curse/long // Necro Sect Usage duration = 330 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dir_setting/curse) + /obj/effect/temp_visual/dir_setting/curse/Initialize(mapload, set_dir) . = ..() if(fades) animate(src, alpha = 0, time = 32) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dir_setting/curse/long) + /obj/effect/temp_visual/dir_setting/curse/long/Initialize(mapload, set_dir) . = ..() if(fades) @@ -166,6 +172,8 @@ pixel_y = -16 duration = 50 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/bsa_splash) + /obj/effect/temp_visual/bsa_splash/Initialize(mapload, dir) . = ..() switch(dir) @@ -211,6 +219,8 @@ desc = "It's a decoy!" duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/decoy) + /obj/effect/temp_visual/decoy/Initialize(mapload, atom/mimiced_atom) . = ..() alpha = initial(alpha) @@ -220,6 +230,8 @@ setDir(mimiced_atom.dir) mouse_opacity = MOUSE_OPACITY_TRANSPARENT +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/decoy/fading) + /obj/effect/temp_visual/decoy/fading/Initialize(mapload, atom/mimiced_atom) . = ..() animate(src, alpha = 0, time = duration) @@ -300,6 +312,8 @@ icon = 'icons/mob/mob.dmi' duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/gib_animation) + /obj/effect/temp_visual/gib_animation/Initialize(mapload, gib_icon) icon_state = gib_icon // Needs to be before ..() so icon is correct . = ..() @@ -311,6 +325,8 @@ icon = 'icons/mob/mob.dmi' duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dust_animation) + /obj/effect/temp_visual/dust_animation/Initialize(mapload, dust_icon) icon_state = dust_icon // Before ..() so the correct icon is flick()'d . = ..() @@ -325,6 +341,8 @@ icon_state = "heal" duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/heal) + /obj/effect/temp_visual/heal/Initialize(mapload, set_color) if(set_color) add_atom_colour(set_color, FIXED_COLOUR_PRIORITY) @@ -367,6 +385,8 @@ icon_state = "impact_bullet" duration = 5 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/impact_effect) + /obj/effect/temp_visual/impact_effect/Initialize(mapload, x, y) pixel_x = x pixel_y = y @@ -423,6 +443,8 @@ /obj/effect/temp_visual/love_heart/invisible icon_state = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/love_heart/invisible) + /obj/effect/temp_visual/love_heart/invisible/Initialize(mapload, mob/seer) . = ..() var/image/I = image(icon = 'icons/effects/effects.dmi', icon_state = "heart", layer = ABOVE_MOB_LAYER, loc = src) @@ -438,6 +460,8 @@ duration = 10 var/shrink = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/bleed) + /obj/effect/temp_visual/bleed/Initialize(mapload, atom/size_calc_target) . = ..() var/size_matrix = matrix() @@ -462,6 +486,8 @@ duration = 5 var/outgoing = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/warp_cube) + /obj/effect/temp_visual/warp_cube/Initialize(mapload, atom/teleporting_atom, warp_color, new_outgoing) . = ..() if(teleporting_atom) @@ -495,6 +521,8 @@ var/status = 0 var/delay = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/constructing_effect) + /obj/effect/constructing_effect/Initialize(mapload, rcd_delay, rcd_status) . = ..() status = rcd_status @@ -537,6 +565,8 @@ icon_state = "smoke" duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/steam) + /obj/effect/temp_visual/steam/Initialize(mapload, steam_direction) . = ..() setDir(steam_direction) @@ -580,6 +610,8 @@ icon_state = "shield" alpha = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/launchpad) + /obj/effect/temp_visual/launchpad/Initialize(mapload, time) duration = time animate(src, time=time, alpha=255) diff --git a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm index 27f49fd93bf77..fab895e04b713 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm @@ -55,6 +55,8 @@ /obj/effect/projectile_lighting var/owner +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/projectile_lighting) + /obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity, owner_key) . = ..() set_light(range, intensity, color) diff --git a/code/game/objects/effects/temporary_visuals/temporary_visual.dm b/code/game/objects/effects/temporary_visuals/temporary_visual.dm index ec4c0b9a7db59..2b6699f0a140f 100644 --- a/code/game/objects/effects/temporary_visuals/temporary_visual.dm +++ b/code/game/objects/effects/temporary_visuals/temporary_visual.dm @@ -13,7 +13,7 @@ if(randomdir) setDir(pick(GLOB.cardinals)) - timerid = QDEL_IN(src, duration) + timerid = QDEL_IN_STOPPABLE(src, duration) /obj/effect/temp_visual/Destroy() . = ..() @@ -28,6 +28,8 @@ /obj/effect/temp_visual/dir_setting randomdir = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dir_setting) + /obj/effect/temp_visual/dir_setting/Initialize(mapload, set_dir) if(set_dir) setDir(set_dir) diff --git a/code/game/objects/effects/wanted_poster.dm b/code/game/objects/effects/wanted_poster.dm index 92f29bd14f302..bece7705b9c29 100644 --- a/code/game/objects/effects/wanted_poster.dm +++ b/code/game/objects/effects/wanted_poster.dm @@ -7,6 +7,8 @@ */ +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/poster/wanted) + /obj/item/poster/wanted icon_state = "rolled_poster" var/postHeaderText = "WANTED" // MAX 7 Characters @@ -21,6 +23,8 @@ postHeaderText = "MISSING" // MAX 7 Characters postHeaderColor = "#0000FF" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/poster/wanted) + /obj/item/poster/wanted/Initialize(mapload, icon/person_icon, wanted_name, description, headerText) . = ..(mapload, new /obj/structure/sign/poster/wanted(src, person_icon, wanted_name, description, headerText, postHeaderColor, background, postName, postDesc)) name = "[postName] ([wanted_name])" @@ -34,6 +38,8 @@ var/posterHeaderText var/posterHeaderColor +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/sign/poster/wanted) + /obj/structure/sign/poster/wanted/Initialize(mapload, icon/person_icon, person_name, description, postHeaderText, postHeaderColor, background, pname, pdesc) . = ..() if(!person_icon) diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 96be0b40ffc06..413c3f36b725b 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -56,6 +56,7 @@ GLOBAL_LIST_INIT(disposal_pipe_recipes, list( new /datum/pipe_info/disposal("Y-Junction", /obj/structure/disposalpipe/junction/yjunction), new /datum/pipe_info/disposal("Sort Junction", /obj/structure/disposalpipe/sorting/mail, PIPE_TRIN_M), new /datum/pipe_info/disposal("Package Junction", /obj/structure/disposalpipe/sorting/wrap, PIPE_TRIN_M), + new /datum/pipe_info/disposal("Unsorted Mail Junction", /obj/structure/disposalpipe/sorting/unsorted, PIPE_TRIN_M), new /datum/pipe_info/disposal("Trunk", /obj/structure/disposalpipe/trunk), new /datum/pipe_info/disposal("Bin", /obj/machinery/disposal/bin, PIPE_ONEDIR), new /datum/pipe_info/disposal("Outlet", /obj/structure/disposaloutlet), diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm index e835909745a4f..01aa2723fba17 100644 --- a/code/game/objects/items/chrono_eraser.dm +++ b/code/game/objects/items/chrono_eraser.dm @@ -180,6 +180,8 @@ var/RPpos = null var/attached = TRUE //if the gun arg isn't included initially, then the chronofield will work without one +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/chrono_field) + /obj/structure/chrono_field/Initialize(mapload, mob/living/target, obj/item/gun/energy/chrono_gun/G) if(target && isliving(target)) if(!G) diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index bd1597fe5942e..b5fde44df77be 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -881,6 +881,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM return (TOXLOSS|OXYLOSS) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/clothing/mask/vape) + /obj/item/clothing/mask/vape/Initialize(mapload, param_color) . = ..() create_reagents(chem_volume, NO_REACT) diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index be38d3ff63d34..cc63d49ef1d1a 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -480,6 +480,8 @@ /obj/machinery/smartfridge/disks = "disks") needs_anchored = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/circuitboard/machine/smartfridge) + /obj/item/circuitboard/machine/smartfridge/Initialize(mapload, new_type) if(new_type) build_path = new_type diff --git a/code/game/objects/items/crab17.dm b/code/game/objects/items/crab17.dm index 60b1769475811..bcdabad928023 100644 --- a/code/game/objects/items/crab17.dm +++ b/code/game/objects/items/crab17.dm @@ -51,6 +51,8 @@ var/player_modifier = 1 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/checkoutmachine) + /obj/structure/checkoutmachine/Initialize(mapload, mob/living/user) bogdanoff = user add_overlay("flaps") @@ -258,6 +260,8 @@ var/obj/structure/checkoutmachine/dump var/mob/living/carbon/human/bogdanoff +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/dumpeetTarget) + /obj/effect/dumpeetTarget/Initialize(mapload, user) . = ..() bogdanoff = user diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 3a283f3b30cd0..e607ef4e1b61a 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -896,6 +896,8 @@ pre_noise = FALSE post_noise = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/toy/crayon/spraycan/gang) + /obj/item/toy/crayon/spraycan/gang/Initialize(mapload, loc, datum/team/gang/G) .=..() if(G) diff --git a/code/game/objects/items/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm index 55939bd4604eb..04f927c1a13a1 100644 --- a/code/game/objects/items/credit_holochip.dm +++ b/code/game/objects/items/credit_holochip.dm @@ -8,6 +8,8 @@ w_class = WEIGHT_CLASS_TINY var/credits = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/holochip) + /obj/item/holochip/Initialize(mapload, amount) . = ..() credits = amount diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 6aa2ee923e4d4..d2aa8aeea28c3 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -205,6 +205,8 @@ icon_state = "medi_holo" duration = 30 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/medical_holosign) + /obj/effect/temp_visual/medical_holosign/Initialize(mapload, creator) . = ..() playsound(loc, 'sound/machines/ping.ogg', 50, FALSE) //make some noise! @@ -604,6 +606,8 @@ var/base_light_range = 4 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/flashlight/spotlight) + /obj/item/flashlight/spotlight/Initialize(mapload, _light_range, _light_power, _light_color) . = ..() if(!isnull(_light_range)) diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm index 7c6d8c1798069..8f93c94e7dc9f 100644 --- a/code/game/objects/items/devices/forcefieldprojector.dm +++ b/code/game/objects/items/devices/forcefieldprojector.dm @@ -90,6 +90,8 @@ armor = list(MELEE = 0, BULLET = 25, LASER = 50, ENERGY = 50, BOMB = 25, BIO = 100, RAD = 100, FIRE = 100, ACID = 100, STAMINA = 0, BLEED = 0) var/obj/item/forcefield_projector/generator +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/projected_forcefield) + /obj/structure/projected_forcefield/Initialize(mapload, obj/item/forcefield_projector/origin) . = ..() generator = origin diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index fe6f1f5c34c79..be51cd20bcecb 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -12,6 +12,8 @@ /obj/item/radio/intercom/unscrewed unscrewed = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/radio/intercom) + /obj/item/radio/intercom/Initialize(mapload, ndir, building) . = ..() var/area/current_area = get_area(src) @@ -160,6 +162,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/radio/intercom, 26) name = "Confessional intercom" anonymize = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/radio/intercom/chapel) + /obj/item/radio/intercom/chapel/Initialize(mapload, ndir, building) . = ..() set_frequency(1481) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index d702c879b1213..912ae6c2c49a7 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -1035,6 +1035,8 @@ GENE SCANNER /// Cooldown for when the extrapolator can be used next. COOLDOWN_DECLARE(usage_cooldown) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/extrapolator) + /obj/item/extrapolator/Initialize(mapload, obj/item/stock_parts/scanning_module/starting_scanner) . = ..() starting_scanner = starting_scanner || default_scanning_module diff --git a/code/game/objects/items/documents.dm b/code/game/objects/items/documents.dm index 9976848bdfc46..0ad1308f364f7 100644 --- a/code/game/objects/items/documents.dm +++ b/code/game/objects/items/documents.dm @@ -37,6 +37,8 @@ var/forgedseal = 0 var/copy_type = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/documents/photocopy) + /obj/item/documents/photocopy/Initialize(mapload, obj/item/documents/copy=null) . = ..() if(copy) diff --git a/code/game/objects/items/grenades/clusterbuster.dm b/code/game/objects/items/grenades/clusterbuster.dm index 08ce67d0e3b1b..1720082bfade3 100644 --- a/code/game/objects/items/grenades/clusterbuster.dm +++ b/code/game/objects/items/grenades/clusterbuster.dm @@ -44,6 +44,8 @@ icon_state = "clusterbang_segment" base_state = "clusterbang_segment" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/grenade/clusterbuster/segment) + /obj/item/grenade/clusterbuster/segment/Initialize(mapload, obj/item/grenade/clusterbuster/base) . = ..() if(base) @@ -74,6 +76,8 @@ ////////////////////////////////// //The payload spawner effect ///////////////////////////////// +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/payload_spawner) + /obj/effect/payload_spawner/Initialize(mapload, type, numspawned) ..() spawn_payload(type, numspawned) diff --git a/code/game/objects/items/grenades/discogrenade.dm b/code/game/objects/items/grenades/discogrenade.dm index 5e6d1caaadac9..f5d1729f0317b 100644 --- a/code/game/objects/items/grenades/discogrenade.dm +++ b/code/game/objects/items/grenades/discogrenade.dm @@ -47,6 +47,8 @@ var/range = 5 var/power = 3 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/grenade/discogrenade/subgrenade) + /obj/item/grenade/discogrenade/subgrenade/Initialize(mapload, duplicate = FALSE) . = ..() active = TRUE diff --git a/code/game/objects/items/implants/implant_track.dm b/code/game/objects/items/implants/implant_track.dm index 1a222cfcc960b..c5dbe1f0471b0 100644 --- a/code/game/objects/items/implants/implant_track.dm +++ b/code/game/objects/items/implants/implant_track.dm @@ -17,7 +17,7 @@ /obj/item/implant/tracking/c38/Initialize(mapload) . = ..() - timerid = QDEL_IN(src, lifespan) + timerid = QDEL_IN_STOPPABLE(src, lifespan) /obj/item/implant/tracking/c38/Destroy() deltimer(timerid) diff --git a/code/game/objects/items/implants/implantuplink.dm b/code/game/objects/items/implants/implantuplink.dm index f80782c77efc6..20db68c4925ba 100644 --- a/code/game/objects/items/implants/implantuplink.dm +++ b/code/game/objects/items/implants/implantuplink.dm @@ -9,6 +9,8 @@ /// The uplink flags of the implant uplink inside, only checked during initialisation so modifying it after initialisation will do nothing var/uplink_flag = UPLINK_TRAITORS +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/implant/uplink) + /obj/item/implant/uplink/Initialize(mapload, owner, uplink_flag) . = ..() AddComponent(/datum/component/uplink, _owner = owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = uplink_flag, starting_tc = starting_tc) @@ -29,6 +31,8 @@ name = "implanter (uplink)" imp_type = /obj/item/implant/uplink +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/implanter/uplink) + /obj/item/implanter/uplink/Initialize(mapload, uplink_flag = UPLINK_TRAITORS) imp = new imp_type(src, null, uplink_flag) . = ..() diff --git a/code/game/objects/items/mjolnir.dm b/code/game/objects/items/mjolnir.dm index 279edbcbe96b3..bf1fd1b30b28f 100644 --- a/code/game/objects/items/mjolnir.dm +++ b/code/game/objects/items/mjolnir.dm @@ -84,6 +84,8 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF var/obj/item/mjolnir/contained +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/anchored_mjolnir) + /obj/structure/anchored_mjolnir/Initialize(mapload, obj/item/mjolnir/god_hammer) . = ..() //Put the hammer inside of ourselves @@ -139,6 +141,8 @@ speed = 0.3 var/obj/item/mjolnir/contained +CREATION_TEST_IGNORE_SUBTYPES(/obj/projectile/mjolnir) + /obj/projectile/mjolnir/Initialize(mapload, obj/item/mjolnir/contained_hammer) . = ..() contained = contained_hammer diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/spear.dm index ae7aaa4f2a6aa..8393847756968 100644 --- a/code/game/objects/items/spear.dm +++ b/code/game/objects/items/spear.dm @@ -65,6 +65,8 @@ icon_state = "spearbomb0" var/obj/item/grenade/explosive = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/spear/explosive) + /obj/item/spear/explosive/Initialize(mapload, obj/item/grenade/G) . = ..() set_explosive(G) diff --git a/code/game/objects/items/stacks/cash.dm b/code/game/objects/items/stacks/cash.dm index c1faa1abc1218..c21543c32940a 100644 --- a/code/game/objects/items/stacks/cash.dm +++ b/code/game/objects/items/stacks/cash.dm @@ -13,6 +13,8 @@ resistance_flags = FLAMMABLE var/value = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/spacecash) + /obj/item/stack/spacecash/Initialize(mapload, new_amount, merge = TRUE, mob/user = null) . = ..() update_desc() diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 91fd05087eeda..0f08b9586cb5c 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -29,6 +29,8 @@ ///How long does it take to apply on yourself? var/self_delay = 2 SECONDS +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/medical) + /obj/item/stack/medical/Initialize(mapload, new_amount, merge, mob/user) . = ..() if(reagent) diff --git a/code/game/objects/items/stacks/ores/ore.dm b/code/game/objects/items/stacks/ores/ore.dm index 555c7db17fc5f..dd43a2a626487 100644 --- a/code/game/objects/items/stacks/ores/ore.dm +++ b/code/game/objects/items/stacks/ores/ore.dm @@ -61,6 +61,8 @@ new refined_type(drop_location(),amountrefined) qdel(src) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/ore) + /obj/item/stack/ore/Initialize(mapload, new_amount, merge = TRUE, mob/user = null) . = ..() pixel_x = base_pixel_x + rand(0,16) - 8 diff --git a/code/game/objects/items/stacks/rods/rods.dm b/code/game/objects/items/stacks/rods/rods.dm index 4ade958ee737a..b059ec4ac85b5 100644 --- a/code/game/objects/items/stacks/rods/rods.dm +++ b/code/game/objects/items/stacks/rods/rods.dm @@ -26,6 +26,8 @@ user.visible_message("[user] begins to stuff \the [src] down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!")//it looks like theyre ur mum return BRUTELOSS +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/rods) + /obj/item/stack/rods/Initialize(mapload, new_amount, merge = TRUE, mob/user = null) . = ..() if(QDELETED(src)) // we can be deleted during merge, check before doing stuff diff --git a/code/game/objects/items/stacks/sheets/mineral/metals.dm b/code/game/objects/items/stacks/sheets/mineral/metals.dm index cdcb2c53bd51d..288e00ed5f055 100644 --- a/code/game/objects/items/stacks/sheets/mineral/metals.dm +++ b/code/game/objects/items/stacks/sheets/mineral/metals.dm @@ -133,6 +133,8 @@ Metals Sheets /obj/item/stack/sheet/brass/get_recipes() return GLOB.brass_recipes +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/sheet/brass) + /obj/item/stack/sheet/brass/Initialize(mapload, new_amount, merge = TRUE) . = ..() pixel_x = 0 @@ -165,6 +167,8 @@ Metals Sheets /obj/item/stack/sheet/bronze/get_recipes() return GLOB.bronze_recipes +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/sheet/bronze) + /obj/item/stack/sheet/bronze/Initialize(mapload, new_amount, merge = TRUE) . = ..() pixel_x = 0 diff --git a/code/game/objects/items/stacks/sheets/organic/leather.dm b/code/game/objects/items/stacks/sheets/organic/leather.dm index 92b2e83b53e15..fdd63ac72fbc9 100644 --- a/code/game/objects/items/stacks/sheets/organic/leather.dm +++ b/code/game/objects/items/stacks/sheets/organic/leather.dm @@ -32,6 +32,8 @@ var/wetness = 30 //Reduced when exposed to high temperautres var/drying_threshold_temperature = 500 //Kelvin to start drying +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/sheet/leather/wetleather) + /obj/item/stack/sheet/leather/wetleather/Initialize(mapload, new_amount, merge) . = ..() AddElement(/datum/element/dryable, /obj/item/stack/sheet/leather) diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm index 6a5ee9649457a..aa9d0e5be76b8 100644 --- a/code/game/objects/items/stacks/sheets/sheets.dm +++ b/code/game/objects/items/stacks/sheets/sheets.dm @@ -17,6 +17,8 @@ ///What type of wall does this sheet spawn var/walltype +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/sheet) + /obj/item/stack/sheet/Initialize(mapload, new_amount, merge) . = ..() pixel_x = rand(-4, 4) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 656598bb82fd2..d1aed8425f457 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -46,6 +46,8 @@ /// Amount of matter for RCD var/matter_amount = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack) + /obj/item/stack/Initialize(mapload, new_amount, merge = TRUE, mob/user = null) if(new_amount != null) amount = new_amount diff --git a/code/game/objects/items/stacks/tiles/light.dm b/code/game/objects/items/stacks/tiles/light.dm index 0fe25744382a5..c5745f6165898 100644 --- a/code/game/objects/items/stacks/tiles/light.dm +++ b/code/game/objects/items/stacks/tiles/light.dm @@ -47,6 +47,8 @@ var/state = 0 merge_type = /obj/item/stack/tile/light +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/tile/light) + /obj/item/stack/tile/light/Initialize(mapload, new_amount, merge = TRUE) . = ..() if(prob(5)) diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index e2dd4821b73b2..c5c0babf017cc 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -29,6 +29,8 @@ /// Allows us to replace the plating we are attacking if our baseturfs are the same. var/replace_plating = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/tile) + /obj/item/stack/tile/Initialize(mapload, new_amount, merge = TRUE, mob/user = null) . = ..() pixel_x = rand(-3, 3) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index b2613c87ab729..f7d829ccb212d 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SELF(/obj) + /obj animate_movement = SLIDE_STEPS speech_span = SPAN_ROBOT diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm index 87c427363bd59..76784ad1e68c6 100644 --- a/code/game/objects/structures/artstuff.dm +++ b/code/game/objects/structures/artstuff.dm @@ -258,6 +258,8 @@ var/desc_with_canvas var/persistence_id +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/sign/painting) + /obj/structure/sign/painting/Initialize(mapload, dir, building) . = ..() SSpersistence.painting_frames += src diff --git a/code/game/objects/structures/bot_elevator.dm b/code/game/objects/structures/bot_elevator.dm index fc4505cb69a8e..4b67869fd924d 100644 --- a/code/game/objects/structures/bot_elevator.dm +++ b/code/game/objects/structures/bot_elevator.dm @@ -8,6 +8,8 @@ var/obj/structure/bot_elevator/up max_integrity = 100 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/bot_elevator) + /obj/structure/bot_elevator/Initialize(mapload, obj/structure/bot_elevator/up, obj/structure/bot_elevator/down) ..() GLOB.bot_elevator += src diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm index a3b28f10edaf0..8bde41cb2b41a 100644 --- a/code/game/objects/structures/crates_lockers/crates/secure.dm +++ b/code/game/objects/structures/crates_lockers/crates/secure.dm @@ -83,6 +83,8 @@ . = ..() . += "It's locked with a privacy lock, and can only be unlocked by the buyer's ID with required access." +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/closet/crate/secure/owned) + /obj/structure/closet/crate/secure/owned/Initialize(mapload, datum/bank_account/_buyer_account) . = ..() buyer_account = _buyer_account diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index a2d071256fd45..7d2cf240fb274 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -403,6 +403,8 @@ /obj/item/showpiece_dummy name = "Cheap replica" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/showpiece_dummy) + /obj/item/showpiece_dummy/Initialize(mapload, path) . = ..() var/obj/item/I = path diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index ece0c709fbcad..27089c6c0b6f8 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -13,6 +13,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29) +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/extinguisher_cabinet) + /obj/structure/extinguisher_cabinet/Initialize(mapload, ndir, building) . = ..() if(building) diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index a8ec76f3e1844..418374583223a 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -68,6 +68,8 @@ H.update_body() H.fully_replace_character_name(null, H.dna.species.random_name(gender)) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/mob_spawn/human/ash_walker) + /obj/effect/mob_spawn/human/ash_walker/Initialize(mapload, datum/team/ashwalkers/ashteam) . = ..() var/area/A = get_area(src) @@ -102,6 +104,8 @@ golems, so that no golem may ever be forced to serve again." banType = ROLE_FREE_GOLEM +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/mob_spawn/human/golem) + /obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null) if(species) //spawners list uses object name to register so this goes before ..() name += " ([initial(species.prefix)])" @@ -355,6 +359,8 @@ assignedrole = "SuperFriend" banType = ROLE_DEMONIC_FRIEND +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/mob_spawn/human/demonic_friend) + /obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell) . = ..() owner = owner_mind diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index ac810ddd96bf2..48a30afa45f17 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -10,6 +10,8 @@ max_integrity = 100 z_flags = Z_BLOCK_OUT_DOWN +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/ladder) + /obj/structure/ladder/Initialize(mapload, obj/structure/ladder/up, obj/structure/ladder/down) ..() if (up) diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index a154713a4ddf6..5f7c20babfaaa 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -14,6 +14,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28) +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/mirror) + /obj/structure/mirror/Initialize(mapload, dir, building) . = ..() if(icon_state == "mirror_broke" && !broken) diff --git a/code/game/objects/structures/petrified_statue.dm b/code/game/objects/structures/petrified_statue.dm index a2a5becf77e27..62987b94c02d6 100644 --- a/code/game/objects/structures/petrified_statue.dm +++ b/code/game/objects/structures/petrified_statue.dm @@ -8,6 +8,8 @@ var/timer = 480 //eventually the person will be freed var/mob/living/petrified_mob +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/statue/petrified) + /obj/structure/statue/petrified/Initialize(mapload, mob/living/L, statue_timer) . = ..() if(statue_timer) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index f6975e17359cb..c81ac90b621f8 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -39,6 +39,8 @@ max_integrity = 100 integrity_failure = 0.33 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/table) + /obj/structure/table/Initialize(mapload, _buildstack) . = ..() if(_buildstack) diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm index c21ce6df8de6a..c4971671b324b 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube.dm @@ -13,6 +13,8 @@ var/exit_delay = 1 var/enter_delay = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/transit_tube) + /obj/structure/transit_tube/Initialize(mapload, newdirection) . = ..() if(newdirection) diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 3467296faf16a..71687e38f612c 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -31,6 +31,8 @@ var/state = "01" //How far the door assembly has progressed +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/windoor_assembly) + /obj/structure/windoor_assembly/Initialize(mapload, loc, set_dir) . = ..() if(set_dir) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index d4e0708538617..7c791af59dd82 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -50,6 +50,8 @@ else . += "The window is unscrewed from the floor, and could be deconstructed by wrenching." +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/window) + /obj/structure/window/Initialize(mapload, direct) . = ..() if(direct) diff --git a/code/game/say.dm b/code/game/say.dm index c2bf41f9c50ea..283f855cf1cd7 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -223,6 +223,8 @@ GLOBAL_LIST_INIT(freqtospan, list( var/obj/item/radio/radio INITIALIZE_IMMEDIATE(/atom/movable/virtualspeaker) +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/virtualspeaker) + /atom/movable/virtualspeaker/Initialize(mapload, atom/movable/M, _radio) . = ..() radio = _radio diff --git a/code/game/turfs/baseturf_skipover.dm b/code/game/turfs/baseturf_skipover.dm index b90b79af2940e..7031e11bcc8f5 100644 --- a/code/game/turfs/baseturf_skipover.dm +++ b/code/game/turfs/baseturf_skipover.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SUBTYPES(/turf/baseturf_skipover) + // This is a typepath to just sit in baseturfs and act as a marker for other things. /turf/baseturf_skipover name = "Baseturf skipover placeholder" @@ -13,6 +15,8 @@ name = "Shuttle baseturf skipover" desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through." +CREATION_TEST_IGNORE_SUBTYPES(/turf/baseturf_bottom) + /turf/baseturf_bottom name = "Z-level baseturf placeholder" desc = "Marker for z-level baseturf, usually resolves to space." diff --git a/code/game/turfs/closed/_closed.dm b/code/game/turfs/closed/_closed.dm index 739d65c64f941..d5c58225c5352 100644 --- a/code/game/turfs/closed/_closed.dm +++ b/code/game/turfs/closed/_closed.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SELF(/turf/closed) + /turf/closed layer = CLOSED_TURF_LAYER opacity = TRUE diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index 478e54c75e0c5..7cc262f13cf33 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SELF(/turf/open) + /turf/open plane = FLOOR_PLANE can_hit = FALSE diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm index caa997873b427..4cef4cfdf32af 100644 --- a/code/game/turfs/open/openspace.dm +++ b/code/game/turfs/open/openspace.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SUBTYPES(/turf/open/openspace) + /turf/open/openspace name = "open space" desc = "Watch your step!" diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index c0a5cc490813b..2dfb686fded00 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -1,5 +1,8 @@ GLOBAL_LIST_EMPTY(station_turfs) GLOBAL_LIST_EMPTY(created_baseturf_lists) + +CREATION_TEST_IGNORE_SELF(/turf) + /turf icon = 'icons/turf/floors.dmi' vis_flags = VIS_INHERIT_ID|VIS_INHERIT_PLANE // Important for interaction with and visualization of openspace. diff --git a/code/game/turfs/turf_texture.dm b/code/game/turfs/turf_texture.dm index a413cd92573ec..25cfdcccef721 100644 --- a/code/game/turfs/turf_texture.dm +++ b/code/game/turfs/turf_texture.dm @@ -27,6 +27,8 @@ ///Associated texture var/datum/turf_texture/parent_texture +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/turf_texture) + /atom/movable/turf_texture/Initialize(mapload, datum/turf_texture/_texture) . = ..() var/datum/turf_texture/texture = new _texture() diff --git a/code/game/world.dm b/code/game/world.dm index 3e3d765fa1148..1cbcc36e48c43 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -163,7 +163,7 @@ GLOBAL_VAR(restart_counter) GLOB.tgui_log = "[GLOB.log_directory]/tgui.log" GLOB.prefs_log = "[GLOB.log_directory]/preferences.log" -#ifdef UNIT_TESTS +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) GLOB.test_log = file("[GLOB.log_directory]/tests.log") start_log(GLOB.test_log) #endif diff --git a/code/modules/admin/view_variables/debug_variable_appearance.dm b/code/modules/admin/view_variables/debug_variable_appearance.dm index faed4f45f41cb..1ed05573f993f 100644 --- a/code/modules/admin/view_variables/debug_variable_appearance.dm +++ b/code/modules/admin/view_variables/debug_variable_appearance.dm @@ -8,6 +8,8 @@ #define ADD_UNUSED_VAR(varlist, thing, varname) if(NAMEOF(##thing, ##varname)) ##varlist += #varname #define RESULT_VARIABLE_NOT_FOUND "_switch_result_variable_not_found" +CREATION_TEST_IGNORE_SELF(/image/appearance) + /// An alias datum that allows us to access and view the variables of an appearance by keeping certain known, yet undocumented, variables that we can access and read in a datum for debugging purposes. /// Kindly do not use this outside of a debugging context. /image/appearance diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm index d2e8650552041..4dac870bbb35e 100644 --- a/code/modules/antagonists/blob/blob_mobs.dm +++ b/code/modules/antagonists/blob/blob_mobs.dm @@ -117,6 +117,8 @@ var/list/datum/disease/spore_diseases = list() flavor_text = FLAVOR_TEXT_GOAL_ANTAG +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/blob/blobspore) + /mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload, var/obj/structure/blob/factory/linked_node) if(istype(linked_node)) factory = linked_node diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index 051efd9ab8a97..025d085e789fd 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -47,6 +47,8 @@ GLOBAL_LIST_EMPTY(blob_nodes) var/list/strain_choices var/need_reroll_strain = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/mob/camera/blob) + /mob/camera/blob/Initialize(mapload, starting_points = 60) validate_location() blob_points = starting_points diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index e7870b87acfaf..49b62ad129736 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -21,6 +21,8 @@ var/atmosblock = FALSE //if the blob blocks atmos and heat spread var/mob/camera/blob/overmind +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/blob) + /obj/structure/blob/Initialize(mapload, owner_overmind) . = ..() if(owner_overmind) diff --git a/code/modules/antagonists/blob/structures/core.dm b/code/modules/antagonists/blob/structures/core.dm index 5ed9ce87c3833..6b563022aeb6c 100644 --- a/code/modules/antagonists/blob/structures/core.dm +++ b/code/modules/antagonists/blob/structures/core.dm @@ -11,6 +11,8 @@ health_regen = 0 //we regen in Life() instead of when pulsed resistance_flags = LAVA_PROOF +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/blob/core) + /obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, placed = 0) GLOB.blob_cores += src START_PROCESSING(SSobj, src) diff --git a/code/modules/antagonists/blob/structures/factory.dm b/code/modules/antagonists/blob/structures/factory.dm index 0bd47d7a1acd5..4c60e01e01aa7 100644 --- a/code/modules/antagonists/blob/structures/factory.dm +++ b/code/modules/antagonists/blob/structures/factory.dm @@ -49,6 +49,8 @@ /obj/structure/blob/factory/lone //A blob factory that functions without a pulses +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/blob/factory/lone) + /obj/structure/blob/factory/lone/Initialize(mapload, owner_overmind) . = ..() START_PROCESSING(SSobj, src) diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 39da9eafa0d9c..04a42574b4f6b 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -165,6 +165,8 @@ var/can_drop = FALSE var/fake = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/melee/arm_blade) + /obj/item/melee/arm_blade/Initialize(mapload,silent,synthetic) . = ..() ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) @@ -233,6 +235,8 @@ requires_wielding = FALSE equip_time = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/gun/magic/tentacle) + /obj/item/gun/magic/tentacle/Initialize(mapload, silent) . = ..() ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) diff --git a/code/modules/antagonists/clock_cult/clockwork_massive.dm b/code/modules/antagonists/clock_cult/clockwork_massive.dm index ee2fed63dd048..8366b0e0e735d 100644 --- a/code/modules/antagonists/clock_cult/clockwork_massive.dm +++ b/code/modules/antagonists/clock_cult/clockwork_massive.dm @@ -241,6 +241,8 @@ GLOBAL_VAR(cult_ratvar) var/ratvar_target var/next_attack_tick +CREATION_TEST_IGNORE_SUBTYPES(/obj/eldritch/ratvar) + /obj/eldritch/ratvar/Initialize(mapload, starting_energy = 50) singularity = WEAKREF(AddComponent( /datum/component/singularity, \ diff --git a/code/modules/antagonists/clock_cult/clockwork_turfs.dm b/code/modules/antagonists/clock_cult/clockwork_turfs.dm index 36a9381d42eac..3c89fb75a8f2e 100644 --- a/code/modules/antagonists/clock_cult/clockwork_turfs.dm +++ b/code/modules/antagonists/clock_cult/clockwork_turfs.dm @@ -598,6 +598,8 @@ for(var/i in 1 to 4) . += new /obj/item/clockwork/alloy_shards/medium/gear_bit(location) +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/window/reinforced/clockwork) + /obj/structure/window/reinforced/clockwork/Initialize(mapload, direct) made_glow = TRUE new /obj/effect/temp_visual/ratvar/window(get_turf(src)) diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index 42da7a651f33e..63b4c7e4485a6 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -357,6 +357,8 @@ var/health_cost = 0 //The amount of health taken from the user when invoking the spell var/datum/action/innate/cult/blood_spell/source +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/melee/blood_magic) + /obj/item/melee/blood_magic/Initialize(mapload, var/spell) . = ..() if(!istype(spell, /datum/action/innate/cult/blood_spell)) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index b5f983fb2db9e..b3fe8d27f1d8c 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -73,6 +73,8 @@ Runes can either be invoked by one's self or with many different cultists. Each var/allow_ghosts = TRUE //Allow ghost cultists (from spirit realm rune) to activate this rune. +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rune) + /obj/effect/rune/Initialize(mapload, set_keyword) . = ..() if(set_keyword) @@ -200,6 +202,8 @@ structure_check() searches for nearby cultist structures required for the invoca invoke_damage = 30 can_be_scribed = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rune/malformed) + /obj/effect/rune/malformed/Initialize(mapload, set_keyword) . = ..() icon_state = "[rand(1,7)]" @@ -384,6 +388,8 @@ structure_check() searches for nearby cultist structures required for the invoca var/listkey +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rune/teleport) + /obj/effect/rune/teleport/Initialize(mapload, set_keyword) . = ..() var/area/A = get_area(src) @@ -513,6 +519,8 @@ structure_check() searches for nearby cultist structures required for the invoca no_scribe_boost = TRUE var/used = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rune/narsie) + /obj/effect/rune/narsie/Initialize(mapload, set_keyword) . = ..() AddElement(/datum/element/point_of_interest) @@ -670,6 +678,8 @@ structure_check() searches for nearby cultist structures required for the invoca var/datum/timedevent/density_timer var/recharging = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rune/wall) + /obj/effect/rune/wall/Initialize(mapload, set_keyword) . = ..() GLOB.wall_runes += src diff --git a/code/modules/antagonists/heretic/magic/mansus_grasp.dm b/code/modules/antagonists/heretic/magic/mansus_grasp.dm index 0fb467dce66db..8009cd6d2fe81 100644 --- a/code/modules/antagonists/heretic/magic/mansus_grasp.dm +++ b/code/modules/antagonists/heretic/magic/mansus_grasp.dm @@ -16,6 +16,8 @@ catchphrase = "R'CH T'H TR'TH!" on_use_sound = 'sound/items/welder.ogg' +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/melee/touch_attack/mansus_fist) + /obj/item/melee/touch_attack/mansus_fist/Initialize(mapload, obj/effect/proc_holder/spell/targeted/touch/_spell) . = ..() AddComponent(/datum/component/effect_remover, \ diff --git a/code/modules/antagonists/heretic/structures/carving_knife.dm b/code/modules/antagonists/heretic/structures/carving_knife.dm index 5977d7504c73d..91493458ade2a 100644 --- a/code/modules/antagonists/heretic/structures/carving_knife.dm +++ b/code/modules/antagonists/heretic/structures/carving_knife.dm @@ -173,6 +173,8 @@ /// Reference to trap owner mob var/datum/weakref/owner +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/trap/eldritch) + /obj/structure/trap/eldritch/Initialize(mapload, new_owner) . = ..() if(new_owner) diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index b90cc238e660f..b71a40fcb5667 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -42,6 +42,8 @@ var/spawn_amt_left = 20 var/spawn_fast = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rend) + /obj/effect/rend/Initialize(mapload, var/spawn_type, var/spawn_amt, var/desc, var/spawn_fast) . = ..() src.spawn_path = spawn_type diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index a3158f5a33ae6..fcc4870e591c7 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -48,6 +48,8 @@ var/first_cycle = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hotspot) + /obj/effect/hotspot/Initialize(mapload, starting_volume, starting_temperature) . = ..() SSair.hotspots += src diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index f02f560c3ab11..7d445ac6eed0a 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -180,6 +180,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/airalarm, 24) var/list/air_vent_info = list() var/list/air_scrub_info = list() +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/airalarm) + /obj/machinery/airalarm/Initialize(mapload, ndir, nbuild) . = ..() wires = new /datum/wires/airalarm(src) diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 24d3e44239728..91c8e8bad1f7e 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -25,6 +25,8 @@ name = "distribution loop gas flow meter" id_tag = ATMOS_GAS_MONITOR_LOOP_DISTRIBUTION +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/meter) + /obj/machinery/meter/Initialize(mapload, new_piping_layer) if(!isnull(new_piping_layer)) target_layer = new_piping_layer diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index d828301a6a128..d1318ee97f90c 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -257,6 +257,8 @@ if(href_list[VV_HK_MODIFY_CANISTER_GAS]) usr.client.modify_canister_gas(src) +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/portable_atmospherics/canister) + /obj/machinery/portable_atmospherics/canister/Initialize(mapload, datum/gas_mixture/existing_mixture) . = ..() if(existing_mixture) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 307a09807e2dc..96cd0d41662b8 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -2,6 +2,8 @@ //To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). +CREATION_TEST_IGNORE_SELF(/obj/effect/mob_spawn) + /obj/effect/mob_spawn name = "Unknown" density = TRUE diff --git a/code/modules/bluespace_anchor/bluespace_anchor.dm b/code/modules/bluespace_anchor/bluespace_anchor.dm index eaf308910f6bc..0ab3ceffc12bf 100644 --- a/code/modules/bluespace_anchor/bluespace_anchor.dm +++ b/code/modules/bluespace_anchor/bluespace_anchor.dm @@ -15,6 +15,8 @@ GLOBAL_LIST_EMPTY(active_bluespace_anchors) var/range = 8 var/power_usage_per_teleport = 1500 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/bluespace_anchor) + /obj/machinery/bluespace_anchor/Initialize(mapload, obj/item/stock_parts/cell/cell) . = ..() //Move the cell diff --git a/code/modules/bluespace_anchor/bluespace_anchor_deployer.dm b/code/modules/bluespace_anchor/bluespace_anchor_deployer.dm index 472606fd3792a..5f7b58b21d807 100644 --- a/code/modules/bluespace_anchor/bluespace_anchor_deployer.dm +++ b/code/modules/bluespace_anchor/bluespace_anchor_deployer.dm @@ -14,6 +14,8 @@ var/obj/item/stock_parts/cell/power_cell +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/bluespace_anchor) + /obj/item/bluespace_anchor/Initialize(mapload, obj/item/stock_parts/cell/cell) . = ..() set_cell(cell) diff --git a/code/modules/cargo/gondolapod.dm b/code/modules/cargo/gondolapod.dm index 64796239d48d3..3d98a10dcc082 100644 --- a/code/modules/cargo/gondolapod.dm +++ b/code/modules/cargo/gondolapod.dm @@ -26,6 +26,8 @@ var/opened = FALSE var/obj/structure/closet/supplypod/centcompod/linked_pod +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/pet/gondola/gondolapod) + /mob/living/simple_animal/pet/gondola/gondolapod/Initialize(mapload, pod) linked_pod = pod name = linked_pod.name diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index 6f7043fd0ac3b..b8e4db5e0f06f 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -90,6 +90,8 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF max_integrity = 20 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/closet/supplypod) + /obj/structure/closet/supplypod/Initialize(mapload, customStyle = FALSE) . = ..() if (!loc) @@ -541,6 +543,8 @@ verticle_offset = initial(verticle_offset) pixel_y = verticle_offset +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/pod_landingzone_effect) + /obj/effect/pod_landingzone_effect name = "" desc = "" @@ -548,11 +552,15 @@ icon_state = "LZ_Slider" layer = PROJECTILE_HIT_THRESHOLD_LAYER +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/pod_landingzone_effect) + /obj/effect/pod_landingzone_effect/Initialize(mapload, obj/structure/closet/supplypod/pod) . = ..() transform = matrix() * 1.5 animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING]) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/pod_landingzone) + /obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to it's location name = "Landing Zone Indicator" desc = "A holographic projection designating the landing zone of something. It's probably best to stand back." @@ -566,6 +574,8 @@ var/obj/effect/pod_landingzone_effect/helper var/list/smoke_effects = new /list(13) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/pod_landingzone) + /obj/effect/pod_landingzone/Initialize(mapload, podParam, single_order = null, clientman) . = ..() if (ispath(podParam)) //We can pass either a path for a pod (as expressconsoles do), or a reference to an instantiated pod (as the centcom_podlauncher does) diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm index 4d2560e591a6f..ffa3f9c25af40 100644 --- a/code/modules/client/client_colour.dm +++ b/code/modules/client/client_colour.dm @@ -53,8 +53,7 @@ else owner.update_client_colour() -/mob - var/list/client_colours = list() +/mob/var/list/client_colours = list() /** * Adds an instance of colour_type to the mob's client_colours list diff --git a/code/modules/client/preferences/submodules/preference_character_preview.dm b/code/modules/client/preferences/submodules/preference_character_preview.dm index f1eefda6bd6b7..11c0556917d8e 100644 --- a/code/modules/client/preferences/submodules/preference_character_preview.dm +++ b/code/modules/client/preferences/submodules/preference_character_preview.dm @@ -51,6 +51,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view/character_preview_view) /// List of clients with this registered to it. var/list/viewing_clients = list() +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/map_view/character_preview_view) + /atom/movable/screen/map_view/character_preview_view/Initialize(mapload, datum/preferences/preferences) . = ..() diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm index a9dbc7b1f9c47..1ae7c244d7020 100644 --- a/code/modules/clothing/spacesuits/chronosuit.dm +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -316,6 +316,8 @@ color = list(1,0,0,0, 0,1,0,0.8, 0,0,1,0.933, 0,0,0,0, 0,0,0,0) appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/chronos_target) + /atom/movable/screen/chronos_target/Initialize(mapload, mob/living/carbon/human/user) if(user) vis_contents += user diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 931b0ad57eaa1..631d64185c887 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -49,6 +49,8 @@ GLOBAL_LIST_EMPTY(all_wormholes) // So we can pick wormholes to teleport to mech_sized = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/portal/wormhole) + /obj/effect/portal/wormhole/Initialize(mapload, _creator, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override) . = ..() GLOB.all_wormholes += src diff --git a/code/modules/fields/timestop.dm b/code/modules/fields/timestop.dm index 7efa781d7818b..30c70c45cbb58 100644 --- a/code/modules/fields/timestop.dm +++ b/code/modules/fields/timestop.dm @@ -18,6 +18,8 @@ var/check_anti_magic = FALSE var/check_holy = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/timestop) + /obj/effect/timestop/Initialize(mapload, radius, time, list/immune_atoms, start = TRUE) //Immune atoms assoc list atom = TRUE . = ..() if(!isnull(time)) diff --git a/code/modules/fields/turf_objects.dm b/code/modules/fields/turf_objects.dm index 99bd1f7d5f711..5edc6300caf77 100644 --- a/code/modules/fields/turf_objects.dm +++ b/code/modules/fields/turf_objects.dm @@ -13,6 +13,8 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT var/datum/proximity_monitor/advanced/parent = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/abstract/proximity_checker/advanced) + /obj/effect/abstract/proximity_checker/advanced/Initialize(mapload, _monitor) if(_monitor) parent = _monitor diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 2247513e0ea2b..dca2b98123818 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -104,6 +104,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( /obj/effect/hallucination/singularity_act() return +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hallucination/simple) + /obj/effect/hallucination/simple/Initialize(mapload, var/mob/living/carbon/T) . = ..() target = T @@ -225,6 +227,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( image_icon = 'icons/mob/alien.dmi' image_state = "alienh_pounce" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hallucination/simple/xeno) + /obj/effect/hallucination/simple/xeno/Initialize(mapload, mob/living/carbon/T) . = ..() name = "alien hunter ([rand(1, 1000)])" @@ -268,6 +272,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( image_icon = 'icons/mob/animal.dmi' image_state = "clown" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hallucination/simple/clown) + /obj/effect/hallucination/simple/clown/Initialize(mapload, mob/living/carbon/T, duration) ..(loc, T) name = pick(GLOB.clown_names) @@ -1070,6 +1076,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( if(image && target.client) target.client.images -= image +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hallucination/danger) + /obj/effect/hallucination/danger/Initialize(mapload, _target) . = ..() target = _target @@ -1083,6 +1091,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( /obj/effect/hallucination/danger/lava name = "lava" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hallucination/danger/lava) + /obj/effect/hallucination/danger/lava/Initialize(mapload, _target) . = ..() var/static/list/loc_connections = list( @@ -1105,6 +1115,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( /obj/effect/hallucination/danger/chasm name = "chasm" +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/hallucination/danger/chasm) + /obj/effect/hallucination/danger/chasm/Initialize(mapload, _target) . = ..() var/static/list/loc_connections = list( diff --git a/code/modules/holoparasite/_holoparasite.dm b/code/modules/holoparasite/_holoparasite.dm index 30c7e5f0eb9bf..47899eb522f63 100644 --- a/code/modules/holoparasite/_holoparasite.dm +++ b/code/modules/holoparasite/_holoparasite.dm @@ -103,6 +103,8 @@ GLOBAL_LIST_EMPTY_TYPED(holoparasites, /mob/living/simple_animal/hostile/holopar /// The tracking beacon component used for the host to track the holoparasite when scouting. var/datum/component/tracking_beacon/tracking_beacon +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/holoparasite) + /mob/living/simple_animal/hostile/holoparasite/Initialize(_mapload, _key, _name, datum/holoparasite_theme/_theme, _accent_color, _notes, datum/mind/_summoner, datum/holoparasite_stats/_stats) . = ..() if(!istype(_summoner)) diff --git a/code/modules/holoparasite/abilities/lesser/snares.dm b/code/modules/holoparasite/abilities/lesser/snares.dm index 68c9314de8347..89440a01e981d 100644 --- a/code/modules/holoparasite/abilities/lesser/snares.dm +++ b/code/modules/holoparasite/abilities/lesser/snares.dm @@ -194,6 +194,8 @@ /atom/movable/screen/holoparasite/snare var/datum/holoparasite_ability/lesser/snare/ability +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite/snare) + /atom/movable/screen/holoparasite/snare/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner, datum/holoparasite_ability/lesser/snare/_ability) . = ..() if(!istype(_ability)) @@ -249,6 +251,8 @@ /// A reference to the holoparasite ability that created this snare. var/datum/holoparasite_ability/lesser/snare/ability +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/snare) + /obj/effect/snare/Initialize(mapload, datum/holoparasite_ability/lesser/snare/_ability) . = ..() if(!istype(_ability)) diff --git a/code/modules/holoparasite/abilities/lesser/telepathy.dm b/code/modules/holoparasite/abilities/lesser/telepathy.dm index 373ef8dbe3d09..ac2c028af7a6f 100644 --- a/code/modules/holoparasite/abilities/lesser/telepathy.dm +++ b/code/modules/holoparasite/abilities/lesser/telepathy.dm @@ -214,6 +214,8 @@ accent_overlay_states = list("telepathy-accent") var/datum/holoparasite_ability/lesser/telepathy/ability +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite/telepathy) + /atom/movable/screen/holoparasite/telepathy/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner, datum/holoparasite_ability/lesser/telepathy/_ability) . = ..() if(!istype(_ability)) diff --git a/code/modules/holoparasite/abilities/lesser/teleport.dm b/code/modules/holoparasite/abilities/lesser/teleport.dm index 2f9ba9f9af793..e88dc5c15036d 100644 --- a/code/modules/holoparasite/abilities/lesser/teleport.dm +++ b/code/modules/holoparasite/abilities/lesser/teleport.dm @@ -230,6 +230,8 @@ /atom/movable/screen/holoparasite/teleport var/datum/holoparasite_ability/lesser/teleport/ability +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite/teleport) + /atom/movable/screen/holoparasite/teleport/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner, datum/holoparasite_ability/lesser/teleport/_ability) . = ..() if(!istype(_ability)) @@ -305,6 +307,8 @@ /// The holoparasite ability that created this beacon. var/datum/holoparasite_ability/lesser/teleport/ability +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/receiving_pad) + /obj/structure/receiving_pad/Initialize(mapload, datum/holoparasite_ability/lesser/teleport/_ability) . = ..() if(!istype(_ability)) @@ -332,6 +336,8 @@ alpha = 0 var/turf/destination +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/holopara_bluespace_tear) + /obj/effect/holopara_bluespace_tear/Initialize(mapload, turf/_destination) . = ..() if(istype(_destination)) diff --git a/code/modules/holoparasite/abilities/major/assassin.dm b/code/modules/holoparasite/abilities/major/assassin.dm index e43ba025d8d4f..808e06c1c42bc 100644 --- a/code/modules/holoparasite/abilities/major/assassin.dm +++ b/code/modules/holoparasite/abilities/major/assassin.dm @@ -128,6 +128,8 @@ icon_state = "backstab" var/datum/holoparasite_ability/major/assassin/ability +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite/toggle_assassin) + /atom/movable/screen/holoparasite/toggle_assassin/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner, datum/holoparasite_ability/major/assassin/_ability) . = ..() if(!istype(_ability)) diff --git a/code/modules/holoparasite/abilities/major/explosive.dm b/code/modules/holoparasite/abilities/major/explosive.dm index 447fdc4ac87a2..c66090cc3c52f 100644 --- a/code/modules/holoparasite/abilities/major/explosive.dm +++ b/code/modules/holoparasite/abilities/major/explosive.dm @@ -304,6 +304,8 @@ /atom/movable/screen/holoparasite/explosive var/datum/holoparasite_ability/major/explosive/ability +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite/explosive) + /atom/movable/screen/holoparasite/explosive/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner, datum/holoparasite_ability/major/explosive/_ability) . = ..() if(!istype(_ability)) diff --git a/code/modules/holoparasite/abilities/major/scout.dm b/code/modules/holoparasite/abilities/major/scout.dm index f03d2f753c42d..f096419eab180 100644 --- a/code/modules/holoparasite/abilities/major/scout.dm +++ b/code/modules/holoparasite/abilities/major/scout.dm @@ -447,6 +447,8 @@ var/static/exit_icon = "cancel" var/datum/holoparasite_ability/major/scout/ability +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/screen/holoparasite/toggle_scout) + /atom/movable/screen/holoparasite/toggle_scout/Initialize(_mapload, mob/living/simple_animal/hostile/holoparasite/_owner, datum/holoparasite_ability/major/scout/_ability) . = ..() if(!istype(_ability)) diff --git a/code/modules/holoparasite/holoparasite_builder.dm b/code/modules/holoparasite/holoparasite_builder.dm index fe4eb2c998e41..e9a6797e316a3 100644 --- a/code/modules/holoparasite/holoparasite_builder.dm +++ b/code/modules/holoparasite/holoparasite_builder.dm @@ -452,6 +452,8 @@ /// Debug mode will simply yoink the user into the newly created holoparasite when enabled. var/debug_mode = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/holoparasite_creator) + /obj/item/holoparasite_creator/Initialize(mapload, datum/holoparasite_theme/theme_override) . = ..() builder = new(src, theme_override || theme, max_points, max_level, uses, debug_mode) diff --git a/code/modules/holoparasite/holoparasite_helpers.dm b/code/modules/holoparasite/holoparasite_helpers.dm index 36e7d66aeb00e..66354584eec6a 100644 --- a/code/modules/holoparasite/holoparasite_helpers.dm +++ b/code/modules/holoparasite/holoparasite_helpers.dm @@ -2,6 +2,8 @@ /// A typecache of objects that the holoparasite where, if the holoparasite's summoner is inside of one of these objects, the holoparasite will not be allowed to manifest. var/static/list/no_manifest_locs +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/holoparasite) + /mob/living/simple_animal/hostile/holoparasite/Initialize(_mapload, _key, _name, datum/holoparasite_theme/_theme, _accent_color, _notes, datum/mind/_summoner, datum/holoparasite_stats/_stats) . = ..() if(!no_manifest_locs) diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 03af9aaad50de..4ae15a1479aaf 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -3,6 +3,8 @@ // Data from the seeds carry over to these grown foods // *********************************************************** +CREATION_TEST_IGNORE_SELF(/obj/item/food/grown) + // Base type. Subtypes are found in /grown dir. Lavaland-based subtypes can be found in mining/ash_flora.dm /obj/item/food/grown icon = 'icons/obj/hydroponics/harvest.dmi' @@ -35,6 +37,8 @@ //Amount of discovery points given for scanning var/discovery_points = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/food/grown) + /obj/item/food/grown/Initialize(mapload, obj/item/seeds/new_seed) if(!tastes) tastes = list("[name]" = 1) //This happens first else the component already inits diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index 18f58db509e2d..3b89cbc066ff9 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -95,6 +95,8 @@ reagents_add = list(/datum/reagent/consumable/nutriment = 0.05) rarity = 30 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/seeds/flower/trumpet) + /obj/item/seeds/flower/trumpet/Initialize(mapload,nogenes) . = ..() if(!nogenes) @@ -285,6 +287,8 @@ grind_results = list(/datum/reagent/consumable/capsaicin = 0, /datum/reagent/consumable/condensedcapsaicin = 0) discovery_points = 300 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/grown/novaflower) + /obj/item/grown/novaflower/Initialize(mapload, obj/item/seeds/new_seed) ..() force = round((5 + seed.potency / 5), 1) diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index beefc089d50ce..bb46eac8a79b0 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -146,6 +146,8 @@ SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "flower_worn") //clover +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/food/grown/grass/shamrock) + /obj/item/food/grown/grass/shamrock/Initialize(mapload, /obj/item/seeds/new_seed) . = ..() if(prob(0.001)) // 0.001% chance to be a clover diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index 6e63746306709..8325f7e12dff4 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -48,6 +48,8 @@ mutatelist = list() reagents_add = list(/datum/reagent/consumable/nutriment = 0.05, /datum/reagent/medicine/silibinin = 0.1) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/seeds/galaxythistle) + /obj/item/seeds/galaxythistle/Initialize(mapload,nogenes) . = ..() if(!nogenes) diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm index f80692b5376a1..a8964617ef06c 100644 --- a/code/modules/hydroponics/grown/mushrooms.dm +++ b/code/modules/hydroponics/grown/mushrooms.dm @@ -211,6 +211,8 @@ growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' reagents_add = list(/datum/reagent/consumable/nutriment = 0.1) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/seeds/chanter/jupitercup) + /obj/item/seeds/chanter/jupitercup/Initialize(mapload,nogenes) . = ..() if(!nogenes) diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm index 480f95742a172..065a2ff7c4323 100644 --- a/code/modules/hydroponics/grown/nettle.dm +++ b/code/modules/hydroponics/grown/nettle.dm @@ -81,6 +81,8 @@ /obj/item/food/grown/nettle/basic seed = /obj/item/seeds/nettle +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/food/grown/nettle/basic) + /obj/item/food/grown/nettle/basic/Initialize(mapload, obj/item/seeds/new_seed) . = ..() force = round((5 + seed.potency / 5), 1) @@ -95,6 +97,8 @@ throwforce = 12 discovery_points = 300 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/food/grown/nettle/death) + /obj/item/food/grown/nettle/death/Initialize(mapload, obj/item/seeds/new_seed) . = ..() force = round((5 + seed.potency / 5), 1) diff --git a/code/modules/hydroponics/growninedible.dm b/code/modules/hydroponics/growninedible.dm index cb29a04c71b58..9c6616cba3871 100644 --- a/code/modules/hydroponics/growninedible.dm +++ b/code/modules/hydroponics/growninedible.dm @@ -10,6 +10,8 @@ var/obj/item/seeds/seed = null // type path, gets converted to item on New(). It's safe to assume it's always a seed item. var/discovery_points = 0 //Amount of discovery points given for scanning +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/grown) + /obj/item/grown/Initialize(mapload, obj/item/seeds/new_seed) . = ..() create_reagents(50) diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 84e49db00a65e..bdf361f941047 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -39,6 +39,8 @@ var/weed_rate = 1 //If the chance below passes, then this many weeds sprout during growth var/weed_chance = 5 //Percentage chance per tray update to grow weeds +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/seeds) + /obj/item/seeds/Initialize(mapload, nogenes = 0) . = ..() pixel_x = base_pixel_y + rand(-8, 8) diff --git a/code/modules/keybindings/focus.dm b/code/modules/keybindings/focus.dm index 205b293e9a1a1..475f663a6bbe8 100644 --- a/code/modules/keybindings/focus.dm +++ b/code/modules/keybindings/focus.dm @@ -1,5 +1,4 @@ -/mob - var/datum/focus //What receives our keyboard inputs. src by default +/mob/var/datum/focus //What receives our keyboard inputs. src by default /mob/proc/set_focus(datum/new_focus) if(focus == new_focus) diff --git a/code/modules/lighting/emissive_blocker.dm b/code/modules/lighting/emissive_blocker.dm index de2547681d897..df31f5f48116b 100644 --- a/code/modules/lighting/emissive_blocker.dm +++ b/code/modules/lighting/emissive_blocker.dm @@ -16,6 +16,8 @@ //Since only render_target handles transform we don't get any applied transform "stacking" appearance_flags = EMISSIVE_APPEARANCE_FLAGS +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/emissive_blocker) + /atom/movable/emissive_blocker/Initialize(mapload, source) . = ..() verbs.Cut() //Cargo culting from lighting object, this maybe affects memory usage? diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index b5c3d7ae0534f..e61e7ee15b17e 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -1,6 +1,6 @@ //Landmarks and other helpers which speed up the mapping process and reduce the number of unique instances/subtypes of items/turf/ect - +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/baseturf_helper) /obj/effect/baseturf_helper //Set the baseturfs of every turf in the /area/ it is placed. name = "baseturf editor" @@ -87,6 +87,7 @@ name = "lavaland baseturf editor" baseturf = /turf/open/lava/smooth/lava_land_surface +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/mapping_helpers) /obj/effect/mapping_helpers icon = 'icons/effects/mapping_helpers.dmi' diff --git a/code/modules/mapping/merge_conflicts.dm b/code/modules/mapping/merge_conflicts.dm index 7bc502e59598a..77c53d6bbea89 100644 --- a/code/modules/mapping/merge_conflicts.dm +++ b/code/modules/mapping/merge_conflicts.dm @@ -2,6 +2,8 @@ // We define it explicitly here to ensure that it shows up on the highest possible plane (while giving off a verbose icon) to aide mappers in resolving these conflicts. // DO NOT USE THIS IN NORMAL MAPPING!!! Linters WILL fail. +CREATION_TEST_IGNORE_SELF(/obj/merge_conflict_marker) + /obj/merge_conflict_marker name = "Merge Conflict Marker - DO NOT USE" icon = 'icons/effects/mapping_helpers.dmi' diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm index 48285fc0e749e..00b067520dc78 100644 --- a/code/modules/mining/equipment/marker_beacons.dm +++ b/code/modules/mining/equipment/marker_beacons.dm @@ -82,6 +82,8 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list( var/remove_speed = 15 var/picked_color +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/marker_beacon) + /obj/structure/marker_beacon/Initialize(mapload, set_color) . = ..() picked_color = set_color diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm index 150ae9fdcab7e..602610f4e6724 100644 --- a/code/modules/mining/equipment/resonator.dm +++ b/code/modules/mining/equipment/resonator.dm @@ -70,6 +70,8 @@ var/creator var/obj/item/resonator/res +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/resonance) + /obj/effect/temp_visual/resonance/Initialize(mapload, set_creator, set_resonator, set_duration) duration = set_duration . = ..() diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 936e5c2db5ef3..0119f7bfff394 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -490,6 +490,8 @@ var/vanish_description = "vanishes from reality" var/can_destroy = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/immortality_talisman) + /obj/effect/immortality_talisman/Initialize(mapload, mob/new_user) . = ..() if(new_user) @@ -556,6 +558,8 @@ STR.max_items = 21 new /obj/item/shared_storage/blue(drop_location(), STR) +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/shared_storage/blue) + /obj/item/shared_storage/blue/Initialize(mapload, datum/component/storage/concrete/master) . = ..() if(!istype(master)) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 30e08eeb84c6e..8d84c8ce389cc 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SELF(/mob/living/carbon) + /mob/living/carbon blood_volume = BLOOD_VOLUME_NORMAL diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 7372fb37f1ef1..b3107518f967b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1161,6 +1161,8 @@ /mob/living/carbon/human/species var/race = null +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/carbon/human/species) + /mob/living/carbon/human/species/Initialize(mapload, specific_race) . = ..() set_species(race || specific_race) diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index 5fb33bdc9412d..2179e274a6775 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -154,6 +154,8 @@ /obj/item/dullahan_relay var/mob/living/owner +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/dullahan_relay) + /obj/item/dullahan_relay/Initialize(mapload,new_owner) . = ..() owner = new_owner diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 21bcb156c3068..36a91c84bffe0 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -866,6 +866,8 @@ var/revive_time = 900 var/mob/living/carbon/human/cloth_golem +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/cloth_pile) + /obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H) . = ..() if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth)) diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 4d5ac25067f0e..3d67252925f1c 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -43,6 +43,8 @@ GLOBAL_LIST_INIT(strippable_monkey_items, create_strippable_list(list( /datum/strippable_item/mob_item_slot/neck ))) +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/carbon/monkey) + /mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner) add_verb(/mob/living/proc/mob_sleep) add_verb(/mob/living/proc/toggle_resting) diff --git a/code/modules/mob/living/inhand_holder.dm b/code/modules/mob/living/inhand_holder.dm index 61a25fa4d2728..5e499032c7da6 100644 --- a/code/modules/mob/living/inhand_holder.dm +++ b/code/modules/mob/living/inhand_holder.dm @@ -12,6 +12,8 @@ ///We are currently releasing the mob held in holder var/releasing = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/clothing/head/mob_holder) + /obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/M, worn_state, head_icon, lh_icon, rh_icon, worn_slot_flags = NONE) . = ..() if(head_icon) @@ -94,6 +96,8 @@ /obj/item/clothing/head/mob_holder/rabbit +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/clothing/head/mob_holder/rabbit) + /obj/item/clothing/head/mob_holder/rabbit/Initialize(mapload, mob/living/M, worn_state, head_icon, lh_icon, rh_icon, worn_slot_flags = NONE) var/mob/living/simple_animal/chicken/rabbit/normal/rabbit = new(src) return ..(mapload, rabbit, rabbit.held_state, rabbit.head_icon, rabbit.held_lh, rabbit.held_rh, rabbit.worn_slot_flags) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index f8aa16bae1d83..d37e30120972c 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -107,6 +107,8 @@ var/obj/effect/overlay/holo_pad_hologram/ai_hologram var/obj/machinery/holopad/current_holopad +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai) + /mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai) default_access_list = get_all_accesses() . = ..() @@ -1038,6 +1040,8 @@ /mob/living/silicon/ai/resist() return +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai/spawned) + /mob/living/silicon/ai/spawned/Initialize(mapload, datum/ai_laws/L, mob/target_ai) if(!target_ai) target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct diff --git a/code/modules/mob/living/simple_animal/bot/atmosbot.dm b/code/modules/mob/living/simple_animal/bot/atmosbot.dm index 6a42640d86618..e2c0491df2269 100644 --- a/code/modules/mob/living/simple_animal/bot/atmosbot.dm +++ b/code/modules/mob/living/simple_animal/bot/atmosbot.dm @@ -72,6 +72,8 @@ // Last time we spoke var/last_speech +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/bot/atmosbot) + /mob/living/simple_animal/bot/atmosbot/Initialize(mapload, new_toolbox_color) . = ..() var/datum/job/J = SSjob.GetJob(JOB_NAME_STATIONENGINEER) diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 7ee271a9b8640..b8a0efe6b6f92 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -45,6 +45,8 @@ var/cell_type = /obj/item/stock_parts/cell var/vest_type = /obj/item/clothing/suit/armor/vest +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/bot/ed209) + /mob/living/simple_animal/bot/ed209/Initialize(mapload,created_name,created_lasercolor) . = ..() if(created_name) diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index e4d3c3819dd22..7f172224c1d4b 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -40,6 +40,8 @@ var/toolbox = /obj/item/storage/toolbox/mechanical var/toolbox_color = "" +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/bot/floorbot) + /mob/living/simple_animal/bot/floorbot/Initialize(mapload, new_toolbox_color) . = ..() toolbox_color = new_toolbox_color diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index e6dcf1a097850..0fb0a9ce5ba91 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -113,6 +113,8 @@ GLOBAL_VAR(medibot_unique_id_gen) return name = t +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/bot/medbot) + /mob/living/simple_animal/bot/medbot/Initialize(mapload, new_skin) . = ..() skin = new_skin diff --git a/code/modules/mob/living/simple_animal/heretic_monsters.dm b/code/modules/mob/living/simple_animal/heretic_monsters.dm index 9783a615f10fa..86f2056b70cc2 100644 --- a/code/modules/mob/living/simple_animal/heretic_monsters.dm +++ b/code/modules/mob/living/simple_animal/heretic_monsters.dm @@ -191,6 +191,8 @@ * * spawn_bodyparts - whether we spawn additional armsy bodies until we reach length. * * worm_length - the length of the worm we're creating. Below 3 doesn't work very well. */ +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/heretic_summon/armsy) + /mob/living/simple_animal/hostile/heretic_summon/armsy/Initialize(mapload, spawn_bodyparts = TRUE, worm_length = 6) . = ..() if(worm_length < 3) @@ -366,6 +368,8 @@ health = 400 melee_damage = 50 +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/heretic_summon/armsy/prime) + /mob/living/simple_animal/hostile/heretic_summon/armsy/prime/Initialize(mapload, spawn_bodyparts = TRUE, worm_length = 9) . = ..() var/matrix/matrix_transformation = matrix() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index c5d467a1e18aa..e01379510971c 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -251,6 +251,8 @@ Difficulty: Medium icon_state = "miner_death" duration = 15 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dir_setting/miner_death) + /obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir) . = ..() INVOKE_ASYNC(src, PROC_REF(fade_out)) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 1ae92cf7d81dc..150149c843497 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -295,6 +295,8 @@ Difficulty: Very Hard duration = 8 var/target +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/at_shield) + /obj/effect/temp_visual/at_shield/Initialize(mapload, new_target) . = ..() target = new_target diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 4b8ad3d0b1e66..d2c818f446ea4 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -413,6 +413,8 @@ Difficulty: Medium light_range = 2 duration = 13 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/lava_warning) + /obj/effect/temp_visual/lava_warning/Initialize(mapload, var/reset_time = 10) . = ..() INVOKE_ASYNC(src, PROC_REF(fall), reset_time) @@ -481,6 +483,8 @@ Difficulty: Medium duration = 10 randomdir = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/dragon_flight) + /obj/effect/temp_visual/dragon_flight/Initialize(mapload, negative) . = ..() INVOKE_ASYNC(src, PROC_REF(flight), negative) @@ -530,6 +534,8 @@ Difficulty: Medium light_range = 2 duration = 9 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/target) + /obj/effect/temp_visual/target/Initialize(mapload, list/flame_hit) . = ..() INVOKE_ASYNC(src, PROC_REF(fall), flame_hit) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 757b359fd578c..f433499acdb4e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -484,6 +484,8 @@ Difficulty: Hard layer = BELOW_MOB_LAYER var/mob/living/caster //who made this, anyway +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/hierophant) + /obj/effect/temp_visual/hierophant/Initialize(mapload, new_caster) . = ..() if(new_caster) @@ -495,6 +497,8 @@ Difficulty: Hard light_range = MINIMUM_USEFUL_LIGHT_RANGE randomdir = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/hierophant/squares) + /obj/effect/temp_visual/hierophant/squares/Initialize(mapload, new_caster) . = ..() if(ismineralturf(loc)) @@ -512,6 +516,8 @@ Difficulty: Hard light_range = MINIMUM_USEFUL_LIGHT_RANGE duration = 100 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/hierophant/wall) + /obj/effect/temp_visual/hierophant/wall/Initialize(mapload, new_caster) . = ..() QUEUE_SMOOTH_NEIGHBORS(src) @@ -537,6 +543,8 @@ Difficulty: Hard var/monster_damage_boost = TRUE var/damage = 10 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/hierophant/chaser) + /obj/effect/temp_visual/hierophant/chaser/Initialize(mapload, new_caster, new_target, new_speed, is_friendly_fire) . = ..() target = new_target @@ -618,6 +626,8 @@ Difficulty: Hard var/friendly_fire_check = FALSE var/bursting = FALSE //if we're bursting and need to hit anyone crossing us +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/hierophant/blast) + /obj/effect/temp_visual/hierophant/blast/Initialize(mapload, new_caster, friendly_fire, defuse) . = ..() friendly_fire_check = friendly_fire diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 4bc042bdb1b11..8961a488312cb 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -141,6 +141,8 @@ GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/ca gold_core_spawnable = NO_SPAWN var/obj/original_of_this = null +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/mimic/copy) + /mob/living/simple_animal/hostile/mimic/copy/Initialize(mapload, obj/original, mob/living/creator, destroy_original = 0, no_googlies = FALSE) . = ..() if (no_googlies) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 92353fa8a5813..b456e6910d606 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -345,6 +345,8 @@ While using this makes the system rely on OnFire, it still gives options for tim var/mob/living/carbon/human/activator = null var/mob/living/simple_animal/hostile/asteroid/elite/ourelite = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/elite_tumor_wall) + /obj/effect/temp_visual/elite_tumor_wall/Initialize(mapload, new_caster) . = ..() QUEUE_SMOOTH_NEIGHBORS(src) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm index a9f97eabaac23..e7990a99204ef 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm @@ -209,6 +209,8 @@ deltimer(timerid) timerid = addtimer(CALLBACK(src, PROC_REF(retract)), 10, TIMER_STOPPABLE) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/goliath_tentacle/broodmother/patch) + /obj/effect/temp_visual/goliath_tentacle/broodmother/patch/Initialize(mapload, new_spawner) . = ..() var/tentacle_locs = spiral_range_turfs(1, get_turf(src)) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm index 5beb5f20f0683..800d667ecefe6 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm @@ -156,6 +156,8 @@ layer = BELOW_MOB_LAYER var/mob/living/spawner +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/goliath_tentacle) + /obj/effect/temp_visual/goliath_tentacle/Initialize(mapload, mob/living/new_spawner) . = ..() for(var/obj/effect/temp_visual/goliath_tentacle/T in loc) @@ -169,6 +171,8 @@ deltimer(timerid) timerid = addtimer(CALLBACK(src, PROC_REF(tripanim)), 7, TIMER_STOPPABLE) +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/goliath_tentacle/original) + /obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner) . = ..() var/list/directions = GLOB.cardinals.Copy() @@ -202,4 +206,4 @@ /obj/effect/temp_visual/goliath_tentacle/proc/retract() icon_state = "Goliath_tentacle_retract" deltimer(timerid) - timerid = QDEL_IN(src, 7) + timerid = QDEL_IN_STOPPABLE(src, 7) diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index 4e14b6fcf58ca..2b6f7b538ccc5 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -57,6 +57,8 @@ // No movement while seen code. +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/statue) + /mob/living/simple_animal/hostile/statue/Initialize(mapload, var/mob/living/creator) . = ..() // Give spells diff --git a/code/modules/mob/living/simple_animal/hostile/stickman.dm b/code/modules/mob/living/simple_animal/hostile/stickman.dm index 686d1ee4e089c..c90c48e7b6bd2 100644 --- a/code/modules/mob/living/simple_animal/hostile/stickman.dm +++ b/code/modules/mob/living/simple_animal/hostile/stickman.dm @@ -48,6 +48,8 @@ icon_dead = "stickdog_dead" mob_biotypes = list(MOB_INORGANIC, MOB_BEAST) +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/hostile/stickman) + /mob/living/simple_animal/hostile/stickman/Initialize(mapload, var/wizard_summoned) . = ..() new /obj/effect/temp_visual/paper_scatter(src) diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 810fced2d03be..ac1d6abcc3476 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -96,6 +96,8 @@ var/transformeffects = SLIME_EFFECT_DEFAULT var/mob/master +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/slime) + /mob/living/simple_animal/slime/Initialize(mapload, new_colour="grey", new_is_adult=FALSE) GLOB.total_slimes++ var/datum/action/innate/slime/feed/F = new @@ -520,6 +522,8 @@ /mob/living/simple_animal/slime/can_be_implanted() return TRUE +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/slime/random) + /mob/living/simple_animal/slime/random/Initialize(mapload, new_colour, new_is_adult) . = ..(mapload, pick(slime_colours), prob(50)) @@ -548,6 +552,8 @@ Friends[user] += SLIME_FRIENDSHIP_ATTACK * 2 master = user +CREATION_TEST_IGNORE_SUBTYPES(/mob/living/simple_animal/slime/rainbow) + /mob/living/simple_animal/slime/rainbow/Initialize(mapload, new_colour="rainbow", new_is_adult) . = ..(mapload, new_colour, new_is_adult) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 90b7b70b2be9f..428c0702bf77f 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SELF(/mob) + /** * The mob, usually meant to be a creature of some type * diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index c01d014d6e9c7..ac8c91c562263 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -1,5 +1,8 @@ // Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code. //TODO: REFACTOR THIS SPAGHETTI CODE, MAKE IT A COMPUTER_HARDWARE COMPONENT OR REMOVE IT + +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/modular_computer/processor) + /obj/item/modular_computer/processor name = "processing unit" desc = "You shouldn't see this. If you do, report it." diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 149a8a81ea1cc..feac0407bc148 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -10,6 +10,8 @@ /obj/item/computer_hardware/battery/get_cell() return battery +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/computer_hardware/battery) + /obj/item/computer_hardware/battery/Initialize(mapload, battery_type) . = ..() if(battery_type) diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 2710bbc6460eb..dac42d19e3b37 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -19,7 +19,7 @@ SSzcopy.queued_overlays += bound_overlay bound_overlay.queued += 1 else if (bound_overlay && !bound_overlay.destruction_timer) - bound_overlay.destruction_timer = QDEL_IN(bound_overlay, 10 SECONDS) + bound_overlay.destruction_timer = QDEL_IN_STOPPABLE(bound_overlay, 10 SECONDS) // Grabs a list of every openspace mimic that's directly or indirectly copying this object. Returns an empty list if none found. /atom/movable/proc/get_associated_mimics() @@ -195,12 +195,12 @@ deltimer(destruction_timer) destruction_timer = null else if (!destruction_timer) - destruction_timer = QDEL_IN(src, 10 SECONDS) + destruction_timer = QDEL_IN_STOPPABLE(src, 10 SECONDS) // Called when the turf we're on is deleted/changed. /atom/movable/openspace/mimic/proc/owning_turf_changed() if (!destruction_timer) - destruction_timer = QDEL_IN(src, 10 SECONDS) + destruction_timer = QDEL_IN_STOPPABLE(src, 10 SECONDS) // Get actual source atom when orbiting /atom/movable/openspace/mimic/get_orbitable() @@ -231,6 +231,8 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT var/turf/delegate +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/openspace/turf_mimic) + /atom/movable/openspace/turf_mimic/Initialize(mapload, ...) . = ..() ASSERT(isturf(loc)) diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index c480793174021..d3e78a97ce3a5 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -91,6 +91,8 @@ name = "paper- infernal contract" contractType = CONTRACT_UNWILLING +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/paper/contract/infernal) + /obj/item/paper/contract/infernal/Initialize(mapload, mob/living/nTarget, datum/mind/nOwner) . = ..() if(!istype(nOwner)) diff --git a/code/modules/paperwork/origami.dm b/code/modules/paperwork/origami.dm index e48aa3d3afd42..28c4960499164 100644 --- a/code/modules/paperwork/origami.dm +++ b/code/modules/paperwork/origami.dm @@ -11,6 +11,8 @@ max_integrity = 50 var/obj/item/paper/internalPaper +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/origami) + /obj/item/origami/Initialize(mapload, obj/item/paper/newPaper) . = ..() pixel_y = base_pixel_y + rand(-8, 8) diff --git a/code/modules/particles/byond_particles/emitter/smoke.dm b/code/modules/particles/byond_particles/emitter/smoke.dm index 7f3c5afc7562b..55c33627ad484 100644 --- a/code/modules/particles/byond_particles/emitter/smoke.dm +++ b/code/modules/particles/byond_particles/emitter/smoke.dm @@ -11,6 +11,8 @@ particles = new/particles/smoke layer = OBJ_LAYER +CREATION_TEST_IGNORE_SUBTYPES(/obj/emitter/flare_smoke) + /obj/emitter/flare_smoke/Initialize(mapload, time, _color) . = ..() add_filter("blur", 1, list(type="blur", size=1.5)) diff --git a/code/modules/photography/photos/frame.dm b/code/modules/photography/photos/frame.dm index 0be0b0ffea9e6..f278c7c8dc92c 100644 --- a/code/modules/photography/photos/frame.dm +++ b/code/modules/photography/photos/frame.dm @@ -78,6 +78,8 @@ #undef FRAME_DEFINE +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/sign/picture_frame) + /obj/structure/sign/picture_frame/Initialize(mapload, dir, building) . = ..() AddElement(/datum/element/art, OK_ART) diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm index e8aaf0ad13594..3f60c4521e5b6 100644 --- a/code/modules/photography/photos/photo.dm +++ b/code/modules/photography/photos/photo.dm @@ -13,6 +13,8 @@ var/datum/picture/picture var/scribble //Scribble on the back. +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/photo) + /obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE) set_picture(P, datum_name, datum_desc, TRUE) return ..() diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index 20d192f5abf41..3dbb06dd7f7b2 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -35,6 +35,8 @@ All the important duct code: ///wheter we just unanchored or drop whatever is in the variable. either is safe var/drop_on_wrench = /obj/item/stack/ducts +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/duct) + /obj/machinery/duct/Initialize(mapload, no_anchor, color_of_duct = "#ffffff", layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) . = ..() @@ -353,6 +355,8 @@ All the important duct code: active = FALSE anchored = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/duct/multilayered) + /obj/machinery/duct/multilayered/Initialize(mapload, no_anchor, color_of_duct, layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) . = ..() update_connects() diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm index 8e65d0b0712d1..aa6a607e35bce 100644 --- a/code/modules/plumbing/plumbers/_plumb_machinery.dm +++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm @@ -22,6 +22,8 @@ ///delay of constructing it throught the plumbing rcd var/rcd_delay = 10 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing) + /obj/machinery/plumbing/Initialize(mapload, bolt = TRUE) . = ..() anchored = bolt @@ -71,6 +73,8 @@ rcd_cost = 5 rcd_delay = 5 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/input) + /obj/machinery/plumbing/input/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_supply, bolt) @@ -85,6 +89,8 @@ rcd_cost = 5 rcd_delay = 5 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/output) + /obj/machinery/plumbing/output/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_demand, bolt) @@ -98,6 +104,8 @@ rcd_cost = 25 rcd_delay = 20 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/tank) + /obj/machinery/plumbing/tank/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/tank, bolt) diff --git a/code/modules/plumbing/plumbers/acclimator.dm b/code/modules/plumbing/plumbers/acclimator.dm index 9eae3d4a48e16..ccc476cce41b8 100644 --- a/code/modules/plumbing/plumbers/acclimator.dm +++ b/code/modules/plumbing/plumbers/acclimator.dm @@ -29,6 +29,8 @@ +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/acclimator) + /obj/machinery/plumbing/acclimator/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/acclimator, bolt) diff --git a/code/modules/plumbing/plumbers/bottle_dispenser.dm b/code/modules/plumbing/plumbers/bottle_dispenser.dm index 0fbd93a536994..eb2527b28f5f6 100644 --- a/code/modules/plumbing/plumbers/bottle_dispenser.dm +++ b/code/modules/plumbing/plumbers/bottle_dispenser.dm @@ -8,6 +8,8 @@ . = ..() . += "Use an open container on it to fill it up!" +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/bottle_dispenser) + /obj/machinery/plumbing/bottle_dispenser/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_demand, bolt) diff --git a/code/modules/plumbing/plumbers/destroyer.dm b/code/modules/plumbing/plumbers/destroyer.dm index 470acfd245bb6..30fb7ee1dc5fb 100644 --- a/code/modules/plumbing/plumbers/destroyer.dm +++ b/code/modules/plumbing/plumbers/destroyer.dm @@ -6,6 +6,8 @@ ///we remove 5 reagents per second var/disposal_rate = 5 +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/disposer) + /obj/machinery/plumbing/disposer/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_demand, bolt) diff --git a/code/modules/plumbing/plumbers/filter.dm b/code/modules/plumbing/plumbers/filter.dm index 8c2249a1d10cb..64b7bb5531729 100644 --- a/code/modules/plumbing/plumbers/filter.dm +++ b/code/modules/plumbing/plumbers/filter.dm @@ -16,6 +16,8 @@ +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/filter) + /obj/machinery/plumbing/filter/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/filter, bolt) diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm index 65ea9570a19b0..ef6bb7eb249a1 100644 --- a/code/modules/plumbing/plumbers/grinder_chemical.dm +++ b/code/modules/plumbing/plumbers/grinder_chemical.dm @@ -10,6 +10,8 @@ active_power_usage = 80 var/eat_dir = SOUTH +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/grinder_chemical) + /obj/machinery/plumbing/grinder_chemical/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_supply, bolt) diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm index 068787ca999c2..09fda4562f477 100644 --- a/code/modules/plumbing/plumbers/reaction_chamber.dm +++ b/code/modules/plumbing/plumbers/reaction_chamber.dm @@ -15,6 +15,8 @@ ///our reagent goal has been reached, so now we lock our inputs and start emptying var/emptying = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/reaction_chamber) + /obj/machinery/plumbing/reaction_chamber/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/reaction_chamber, bolt) diff --git a/code/modules/plumbing/plumbers/splitters.dm b/code/modules/plumbing/plumbers/splitters.dm index 0c098a3e14920..b7bbe2d340c64 100644 --- a/code/modules/plumbing/plumbers/splitters.dm +++ b/code/modules/plumbing/plumbers/splitters.dm @@ -18,6 +18,8 @@ +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/splitter) + /obj/machinery/plumbing/splitter/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/splitter, bolt) diff --git a/code/modules/plumbing/plumbers/synthesizer.dm b/code/modules/plumbing/plumbers/synthesizer.dm index 045eccf507d95..c0337c487235b 100644 --- a/code/modules/plumbing/plumbers/synthesizer.dm +++ b/code/modules/plumbing/plumbers/synthesizer.dm @@ -52,6 +52,8 @@ +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/plumbing/synthesizer) + /obj/machinery/plumbing/synthesizer/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_supply, bolt) diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm index 653241b3085b3..2133d5b7d2da9 100644 --- a/code/modules/point/point.dm +++ b/code/modules/point/point.dm @@ -93,6 +93,8 @@ plane = POINT_PLANE duration = POINT_TIME +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/point) + /obj/effect/temp_visual/point/Initialize(mapload, set_invis = 0) . = ..() var/atom/old_loc = loc diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 2ae1f7bdf31b0..0f6aed1dec8bc 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -84,6 +84,8 @@ By design, d1 is the smallest direction and d2 is the highest color = "#ffffff" // the power cable object +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/cable) + /obj/structure/cable/Initialize(mapload, param_color) . = ..() @@ -522,6 +524,8 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai /obj/item/stack/cable_coil/get_recipes() return GLOB.cable_coil_recipes +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/cable_coil) + /obj/item/stack/cable_coil/Initialize(mapload, new_amount = null, param_color = null) . = ..() diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 06d87c838a6e4..13a1f4bd3dee4 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -33,6 +33,8 @@ /obj/item/stock_parts/cell/get_cell() return src +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stock_parts/cell) + /obj/item/stock_parts/cell/Initialize(mapload, override_maxcharge) . = ..() START_PROCESSING(SSobj, src) diff --git a/code/modules/power/lighting/light_construct.dm b/code/modules/power/lighting/light_construct.dm index 47584ee0d1fe6..59770df16f9a5 100644 --- a/code/modules/power/lighting/light_construct.dm +++ b/code/modules/power/lighting/light_construct.dm @@ -16,6 +16,8 @@ var/cell_connectors = TRUE +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/light_construct) + /obj/structure/light_construct/Initialize(mapload, ndir, building) . = ..() if(building) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index e7d57f1962e6a..7d6b4d6f40d81 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -32,6 +32,8 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION +CREATION_TEST_IGNORE_SUBTYPES(/obj/anomaly/singularity) + /obj/anomaly/singularity/Initialize(mapload, starting_energy = 50) . = ..() START_PROCESSING(SSsinguloprocess, src) @@ -418,6 +420,8 @@ /obj/anomaly/singularity/deadchat_controlled move_self = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/anomaly/singularity/deadchat_controlled) + /obj/anomaly/singularity/deadchat_controlled/Initialize(mapload, starting_energy) . = ..() AddComponent(/datum/component/deadchat_control, DEMOCRACY_MODE, list( diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 14d1bc6776851..7a7705f6b9c92 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -23,6 +23,8 @@ var/turn_angle = 0 var/obj/machinery/power/solar_control/control = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/power/solar) + /obj/machinery/power/solar/Initialize(mapload, obj/item/solar_assembly/S) . = ..() Make(S) diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index f1a2d9f89c515..b3a2a1ea4458b 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -28,6 +28,8 @@ var/energy_to_raise = 32 var/energy_to_lower = -20 +CREATION_TEST_IGNORE_SUBTYPES(/obj/anomaly/energy_ball) + /obj/anomaly/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE) . = ..() energy = starting_energy diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index 64083fee19774..79f18024c8052 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -17,6 +17,8 @@ var/sun_angle = 0 // sun angle as set by sun datum var/obj/machinery/power/solar_control/control = null +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/power/tracker) + /obj/machinery/power/tracker/Initialize(mapload, obj/item/solar_assembly/S) . = ..() Make(S) diff --git a/code/modules/projectiles/ammunition/energy/portal.dm b/code/modules/projectiles/ammunition/energy/portal.dm index 4928786136589..f2ac21e445d38 100644 --- a/code/modules/projectiles/ammunition/energy/portal.dm +++ b/code/modules/projectiles/ammunition/energy/portal.dm @@ -11,6 +11,8 @@ projectile_type = /obj/projectile/beam/wormhole/orange select_name = "orange" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/ammo_casing/energy/wormhole) + /obj/item/ammo_casing/energy/wormhole/Initialize(mapload, obj/item/gun/energy/wormhole_projector/wh) . = ..() gun = WEAKREF(wh) diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm index 820fa67248cf7..741be266bf6bb 100644 --- a/code/modules/projectiles/guns/misc/blastcannon.dm +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -122,6 +122,8 @@ var/hugbox = TRUE range = 150 +CREATION_TEST_IGNORE_SUBTYPES(/obj/projectile/blastwave) + /obj/projectile/blastwave/Initialize(mapload, _h, _m, _l) heavyr = _h mediumr = _m diff --git a/code/modules/projectiles/projectile/special/wormhole.dm b/code/modules/projectiles/projectile/special/wormhole.dm index 1c2ec6906e41c..4f78a0bcbe263 100644 --- a/code/modules/projectiles/projectile/special/wormhole.dm +++ b/code/modules/projectiles/projectile/special/wormhole.dm @@ -18,6 +18,8 @@ name = "orange bluespace beam" color = "#FF6600" +CREATION_TEST_IGNORE_SUBTYPES(/obj/projectile/beam/wormhole) + /obj/projectile/beam/wormhole/Initialize(mapload, obj/item/ammo_casing/energy/wormhole/casing) . = ..() if(casing) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index b87527b7b4986..97516887556a5 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -31,6 +31,8 @@ ///Does this container prevent grinding? var/prevent_grinding = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/reagent_containers) + /obj/item/reagent_containers/Initialize(mapload, vol) . = ..() if(isnum_safe(vol) && vol > 0) diff --git a/code/modules/reagents/reagent_containers/chem_heirloom.dm b/code/modules/reagents/reagent_containers/chem_heirloom.dm index c930cf8f0a5c8..3cb62b423d421 100644 --- a/code/modules/reagents/reagent_containers/chem_heirloom.dm +++ b/code/modules/reagents/reagent_containers/chem_heirloom.dm @@ -12,6 +12,8 @@ var/datum/reagent/rand_cont //Reagent of choice var/datum/callback/roundend_callback +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/reagent_containers/glass/chem_heirloom) + /obj/item/reagent_containers/glass/chem_heirloom/Initialize(mapload, vol) ..() volume = 0 diff --git a/code/modules/recycling/conveyor.dm b/code/modules/recycling/conveyor.dm index b9bcac6cc8fe0..f48c9109b5dca 100644 --- a/code/modules/recycling/conveyor.dm +++ b/code/modules/recycling/conveyor.dm @@ -36,6 +36,8 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) // Auto conveyour is always on unless unpowered +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/conveyor/auto) + /obj/machinery/conveyor/auto/Initialize(mapload, newdir) . = ..() set_operating(TRUE) @@ -48,6 +50,8 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) set_operating(TRUE) // create a conveyor +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/conveyor) + /obj/machinery/conveyor/Initialize(mapload, newdir, newid) . = ..() if(newdir) @@ -174,7 +178,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) if(machine_stat & NOPOWER) set_operating(FALSE) return FALSE - + if(!operating) //If we're on, start conveying so moveloops on our tile can be refreshed if they stopped for some reason return for(var/atom/movable/movable in get_turf(src)) @@ -316,6 +320,8 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/conveyor) var/id = "" // must match conveyor IDs to control them +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/conveyor_switch) + /obj/machinery/conveyor_switch/Initialize(mapload, newid) . = ..() if (newid) @@ -482,6 +488,8 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/conveyor_switch) var/id = "" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/stack/conveyor) + /obj/item/stack/conveyor/Initialize(mapload, new_amount, merge = TRUE, mob/user = null, _id) . = ..() id = _id diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 9096d84b85e79..a8d1667bdd568 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -26,6 +26,8 @@ // create a new disposal // find the attached trunk (if present) and init gas resvr. +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/disposal) + /obj/machinery/disposal/Initialize(mapload, obj/structure/disposalconstruct/make_from) . = ..() @@ -430,6 +432,8 @@ return ..() +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/disposal/deliveryChute) + /obj/machinery/disposal/deliveryChute/Initialize(mapload, obj/structure/disposalconstruct/make_from) . = ..() trunk = locate() in loc diff --git a/code/modules/recycling/disposal/construction.dm b/code/modules/recycling/disposal/construction.dm index 8ddcdd5b70b85..106eff2762c6d 100644 --- a/code/modules/recycling/disposal/construction.dm +++ b/code/modules/recycling/disposal/construction.dm @@ -19,6 +19,8 @@ return density = anchorvalue ? initial(pipe_type.density) : FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/disposalconstruct) + /obj/structure/disposalconstruct/Initialize(mapload, _pipe_type, _dir = SOUTH, flip = FALSE, obj/make_from) . = ..() if(make_from) diff --git a/code/modules/recycling/disposal/holder.dm b/code/modules/recycling/disposal/holder.dm index 4ff466db0f06c..17c6872425561 100644 --- a/code/modules/recycling/disposal/holder.dm +++ b/code/modules/recycling/disposal/holder.dm @@ -16,6 +16,7 @@ var/destinationTag = NONE // changes if contains a delivery container var/tomail = FALSE // contains wrapped package var/hasmob = FALSE // contains a mob + var/unsorted = TRUE // have we been sorted yet? /obj/structure/disposalholder/Destroy() QDEL_NULL(gas) diff --git a/code/modules/recycling/disposal/outlet.dm b/code/modules/recycling/disposal/outlet.dm index 7cff1fcfd4fa4..64e511cd6e037 100644 --- a/code/modules/recycling/disposal/outlet.dm +++ b/code/modules/recycling/disposal/outlet.dm @@ -13,6 +13,8 @@ var/start_eject = 0 var/eject_range = 2 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/disposaloutlet) + /obj/structure/disposaloutlet/Initialize(mapload, obj/structure/disposalconstruct/make_from) . = ..() diff --git a/code/modules/recycling/disposal/pipe.dm b/code/modules/recycling/disposal/pipe.dm index 79c16ade1c3c1..95cf2c7a724d8 100644 --- a/code/modules/recycling/disposal/pipe.dm +++ b/code/modules/recycling/disposal/pipe.dm @@ -17,6 +17,8 @@ var/flip_type // If set, the pipe is flippable and becomes this type when flipped +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/disposalpipe) + /obj/structure/disposalpipe/Initialize(mapload, obj/structure/disposalconstruct/make_from) . = ..() diff --git a/code/modules/recycling/disposal/pipe_sorting.dm b/code/modules/recycling/disposal/pipe_sorting.dm index 4aab953e83306..2a85ae8f0efbe 100644 --- a/code/modules/recycling/disposal/pipe_sorting.dm +++ b/code/modules/recycling/disposal/pipe_sorting.dm @@ -8,10 +8,17 @@ /obj/structure/disposalpipe/sorting/nextdir(obj/structure/disposalholder/H) var/sortdir = dpdir & ~(dir | turn(dir, 180)) - if(H.dir != sortdir) // probably came from the negdir - if(check_sorting(H)) // if destination matches filtered type... - return sortdir // exit through sortdirection - + var/input_direction = dir + // probably came from the negdir + if(H.dir == input_direction) + // if destination matches filtered type... + if(check_sorting(H)) + H.unsorted = FALSE + // exit through sortdirection + return sortdir + // If we are entering backwards, continue onwards + if (H.dir == turn(input_direction, 180)) + return H.dir // go with the flow to positive direction return dir @@ -23,7 +30,7 @@ // Mail sorting junction, uses package tags to sort objects. /obj/structure/disposalpipe/sorting/mail - desc = "An underfloor disposal pipe that sorts wrapped objects based on their destination tags." + desc = "An underfloor disposal pipe that sorts wrapped objects based on their destination tags. Objects passing through it become sorted." flip_type = /obj/structure/disposalpipe/sorting/mail/flip var/sortType = 0 // sortType is to be set in map editor. @@ -82,7 +89,7 @@ // Wrap sorting junction, sorts objects destined for the mail office mail table (tomail = 1) /obj/structure/disposalpipe/sorting/wrap name = "package sorting disposal pipe" - desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects." + desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects. Objects passing through it become sorted." flip_type = /obj/structure/disposalpipe/sorting/wrap/flip initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP @@ -93,3 +100,19 @@ icon_state = "pipe-j2s" flip_type = /obj/structure/disposalpipe/sorting/wrap initialize_dirs = DISP_DIR_LEFT | DISP_DIR_FLIP + +// Unsorted junction, will divert things based on whether or not they have been sorted. +/obj/structure/disposalpipe/sorting/unsorted + name = "unsorted sorting disposal pipe" + desc = "An underfloor disposal pipe which sorts sorted and unsorted objects. Objects passing through it become sorted." + flip_type = /obj/structure/disposalpipe/sorting/unsorted/flip + initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP + +/obj/structure/disposalpipe/sorting/unsorted/check_sorting(obj/structure/disposalholder/H) + return H.unsorted && (H.destinationTag || H.tomail) + +/obj/structure/disposalpipe/sorting/unsorted/flip + icon_state = "pipe-j2s" + flip_type = /obj/structure/disposalpipe/sorting/unsorted + initialize_dirs = DISP_DIR_LEFT | DISP_DIR_FLIP + diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm index 80754b4603e4a..56f572ee823d8 100644 --- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm +++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm @@ -54,6 +54,8 @@ Slimecrossing Armor anchored = TRUE max_integrity = 10 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/light_prism) + /obj/structure/light_prism/Initialize(mapload, var/newcolor) . = ..() color = newcolor diff --git a/code/modules/research/xenobiology/crossbreeding/_structures.dm b/code/modules/research/xenobiology/crossbreeding/_structures.dm index 831797876a3d6..b2b7bc19e052a 100644 --- a/code/modules/research/xenobiology/crossbreeding/_structures.dm +++ b/code/modules/research/xenobiology/crossbreeding/_structures.dm @@ -352,6 +352,8 @@ GLOBAL_LIST_EMPTY(bluespace_slime_crystals) var/max_stage = 5 var/datum/weakref/pylon +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/cerulean_slime_crystal) + /obj/structure/cerulean_slime_crystal/Initialize(mapload, obj/structure/slime_crystal/cerulean/master_pylon) . = ..() if(istype(master_pylon)) diff --git a/code/modules/research/xenobiology/crossbreeding/gentle.dm b/code/modules/research/xenobiology/crossbreeding/gentle.dm index f44cd2a4c7287..442a20345a63d 100644 --- a/code/modules/research/xenobiology/crossbreeding/gentle.dm +++ b/code/modules/research/xenobiology/crossbreeding/gentle.dm @@ -1,3 +1,5 @@ +CREATION_TEST_IGNORE_SELF(/obj/item/slimecross/gentle) + /obj/item/slimecross/gentle name = "gentle extract" desc = "It pulses slowly, as if breathing." diff --git a/code/modules/research/xenobiology/crossbreeding/recurring.dm b/code/modules/research/xenobiology/crossbreeding/recurring.dm index 7b49ad37866dc..4b4671155434f 100644 --- a/code/modules/research/xenobiology/crossbreeding/recurring.dm +++ b/code/modules/research/xenobiology/crossbreeding/recurring.dm @@ -3,6 +3,9 @@ Recurring extracts: Generates a new charge every few seconds. If depleted of its' last charge, stops working. */ + +CREATION_TEST_IGNORE_SELF(/obj/item/slimecross/recurring) + /obj/item/slimecross/recurring name = "recurring extract" desc = "A tiny, glowing core, wrapped in several layers of goo." diff --git a/code/modules/security/genpop.dm b/code/modules/security/genpop.dm index c0833b6db92ab..7c809e3c61ea0 100644 --- a/code/modules/security/genpop.dm +++ b/code/modules/security/genpop.dm @@ -370,6 +370,8 @@ icon_state = "power_mod" desc = "Central processing unit for the prisoner interface." +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/genpop_interface) + /obj/machinery/genpop_interface/Initialize(mapload, nbuild) . = ..() update_icon() @@ -696,6 +698,8 @@ GLOBAL_LIST_EMPTY(prisoner_ids) var/crime = null //What you in for mate? var/atom/assigned_locker = null //Where's our stuff then guv? +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/card/id/prisoner) + /obj/item/card/id/prisoner/Initialize(mapload, _sentence, _crime, _name) . = ..() GLOB.prisoner_ids += src diff --git a/code/modules/shuttle/bluespace_shuttle_pod/pod_computer.dm b/code/modules/shuttle/bluespace_shuttle_pod/pod_computer.dm index 4b44692211efe..33e560f6bb4b5 100644 --- a/code/modules/shuttle/bluespace_shuttle_pod/pod_computer.dm +++ b/code/modules/shuttle/bluespace_shuttle_pod/pod_computer.dm @@ -3,6 +3,8 @@ circuit = /obj/item/circuitboard/computer/shuttle/flight_control var/shuttle_named = FALSE +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/shuttle_flight/custom_shuttle/bluespace_pod) + /obj/machinery/computer/shuttle_flight/custom_shuttle/bluespace_pod/Initialize(mapload, obj/item/circuitboard/C) . = ..() var/static/pod_shuttles = 0 diff --git a/code/modules/shuttle/ripple.dm b/code/modules/shuttle/ripple.dm index 824c1843bac71..4271493064edc 100644 --- a/code/modules/shuttle/ripple.dm +++ b/code/modules/shuttle/ripple.dm @@ -11,6 +11,8 @@ mouse_opacity = MOUSE_OPACITY_ICON alpha = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/abstract/ripple) + /obj/effect/abstract/ripple/Initialize(mapload, time_left) . = ..() animate(src, alpha=255, time=time_left) diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index a8461d69d042d..a9f3c0d348a7e 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -10,6 +10,8 @@ GLOBAL_LIST_INIT(shuttle_turf_blacklist, typecacheof(list( /turf/open/floor/dock/drydock ))) +CREATION_TEST_IGNORE_SUBTYPES(/obj/docking_port) + //NORTH default dir /obj/docking_port invisibility = INVISIBILITY_ABSTRACT diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm index 9b29511e19db7..a800ab7839c50 100644 --- a/code/modules/shuttle/special.dm +++ b/code/modules/shuttle/special.dm @@ -184,6 +184,8 @@ max_integrity = 1000 var/boot_dir = 1 +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/table/wood/bar) + /obj/structure/table/wood/bar/Initialize(mapload, _buildstack) . = ..() var/static/list/loc_connections = list( diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm index 9a43f7fdfc721..bbd87d275020d 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm @@ -154,6 +154,8 @@ var/datum/proximity_monitor/monitor var/datum/callback/callback +CREATION_TEST_IGNORE_SUBTYPES(/atom/movable/proximity_monitor_holder) + /atom/movable/proximity_monitor_holder/Initialize(mapload, datum/proximity_monitor/_monitor, datum/callback/_callback) monitor = _monitor callback = _callback diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/artifact_defenses.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/artifact_defenses.dm index 29ae9086f5f28..3a566db4119ce 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/artifact_defenses.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/artifact_defenses.dm @@ -115,6 +115,8 @@ /obj/effect/temp_visual/hierophant/blast/defenders/emp duration = 1 SECONDS +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/temp_visual/hierophant/blast/defenders/emp) + /obj/effect/temp_visual/hierophant/blast/defenders/emp/Initialize(mapload, new_caster, friendly_fire) . = ..() addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(empulse), src.loc, 1, 2), 1 SECONDS) diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_computer.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_computer.dm index b26b920412ad2..1746797d8149b 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_computer.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_computer.dm @@ -10,6 +10,8 @@ GLOBAL_LIST_EMPTY(objective_computers) circuit = /obj/item/circuitboard/computer/objective var/list/viewing_mobs = list() +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/objective) + /obj/machinery/computer/objective/Initialize(mapload, obj/item/circuitboard/C) . = ..() GLOB.objective_computers += src diff --git a/code/modules/shuttle/super_cruise/shuttle_components/shuttle_console.dm b/code/modules/shuttle/super_cruise/shuttle_components/shuttle_console.dm index 0516a799feb7e..a5ebce4303b02 100644 --- a/code/modules/shuttle/super_cruise/shuttle_components/shuttle_console.dm +++ b/code/modules/shuttle/super_cruise/shuttle_components/shuttle_console.dm @@ -34,6 +34,8 @@ GLOBAL_VAR_INIT(shuttle_docking_jammed, FALSE) //Our orbital body. var/datum/orbital_object/shuttle/shuttleObject +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/shuttle_flight) + /obj/machinery/computer/shuttle_flight/Initialize(mapload, obj/item/circuitboard/C) . = ..() valid_docks = params2list(possible_destinations) diff --git a/code/modules/shuttle/super_cruise/shuttle_components/shuttle_docking.dm b/code/modules/shuttle/super_cruise/shuttle_components/shuttle_docking.dm index 7e96bfc7634b8..a48af3f394b16 100644 --- a/code/modules/shuttle/super_cruise/shuttle_components/shuttle_docking.dm +++ b/code/modules/shuttle/super_cruise/shuttle_components/shuttle_docking.dm @@ -32,6 +32,8 @@ ///Camera action button to move down a Z level var/datum/action/innate/camera_multiz_down/move_down_action = new +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/computer/shuttle_flight) + /obj/machinery/computer/shuttle_flight/Initialize(mapload, obj/item/circuitboard/C) . = ..() GLOB.navigation_computers += src @@ -362,6 +364,8 @@ var/list/placement_images = list() var/list/placed_images = list() +CREATION_TEST_IGNORE_SUBTYPES(/mob/camera/ai_eye/remote/shuttle_docker) + /mob/camera/ai_eye/remote/shuttle_docker/Initialize(mapload, obj/machinery/computer/camera_advanced/origin) src.origin = origin return ..() diff --git a/code/modules/spells/spell_types/forcewall.dm b/code/modules/spells/spell_types/forcewall.dm index aab1f8961772a..2ac9260ea4f86 100644 --- a/code/modules/spells/spell_types/forcewall.dm +++ b/code/modules/spells/spell_types/forcewall.dm @@ -26,6 +26,8 @@ /obj/effect/forcefield/wizard var/mob/wizard +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/forcefield/wizard) + /obj/effect/forcefield/wizard/Initialize(mapload, ntimeleft, mob/summoner) . = ..() wizard = summoner diff --git a/code/modules/spells/spell_types/godhand.dm b/code/modules/spells/spell_types/godhand.dm index 6ac95d96eb844..a8b8e14526c53 100644 --- a/code/modules/spells/spell_types/godhand.dm +++ b/code/modules/spells/spell_types/godhand.dm @@ -17,6 +17,8 @@ throw_speed = 0 var/charges = 1 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/melee/touch_attack) + /obj/item/melee/touch_attack/Initialize(mapload, obj/effect/proc_holder/spell/targeted/touch/_spell) . = ..() ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) @@ -197,6 +199,8 @@ catchphrase = null var/datum/mutation/parent_mutation +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/melee/touch_attack/mutation) + /obj/item/melee/touch_attack/mutation/Initialize(_mapload, obj/effect/proc_holder/spell/targeted/touch/_spell, datum/mutation/_parent) . = ..() if(!istype(_parent)) diff --git a/code/modules/spells/spell_types/lesserlichdom.dm b/code/modules/spells/spell_types/lesserlichdom.dm index 93cd0c16a9ce1..21f22ee5640f7 100644 --- a/code/modules/spells/spell_types/lesserlichdom.dm +++ b/code/modules/spells/spell_types/lesserlichdom.dm @@ -89,6 +89,8 @@ var/static/active_phylacteries = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/lesserphylactery) + /obj/item/lesserphylactery/Initialize(mapload, datum/mind/newmind) . = ..() mind = newmind diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm index 65ceadb56e6a2..7dd9a7c7ab02d 100644 --- a/code/modules/spells/spell_types/lichdom.dm +++ b/code/modules/spells/spell_types/lichdom.dm @@ -91,6 +91,8 @@ var/static/active_phylacteries = 0 +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/phylactery) + /obj/item/phylactery/Initialize(mapload, datum/mind/newmind) . = ..() mind = newmind diff --git a/code/modules/spells/spell_types/shapeshift.dm b/code/modules/spells/spell_types/shapeshift.dm index 4c01f64823dd3..70fea8d8c39a4 100644 --- a/code/modules/spells/spell_types/shapeshift.dm +++ b/code/modules/spells/spell_types/shapeshift.dm @@ -92,6 +92,8 @@ var/datum/soullink/shapeshift/slink var/obj/effect/proc_holder/spell/targeted/shapeshift/source +CREATION_TEST_IGNORE_SUBTYPES(/obj/shapeshift_holder) + /obj/shapeshift_holder/Initialize(mapload,obj/effect/proc_holder/spell/targeted/shapeshift/source,mob/living/caster, convert_damage = FALSE) . = ..() src.source = source diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm index 9af90c350c286..4586d7635e62b 100644 --- a/code/modules/spells/spell_types/touch_attacks.dm +++ b/code/modules/spells/spell_types/touch_attacks.dm @@ -89,6 +89,8 @@ clothes_req = FALSE var/datum/mutation/parent_mutation +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/proc_holder/spell/targeted/touch/mutation) + /obj/effect/proc_holder/spell/targeted/touch/mutation/Initialize(_mapload, datum/mutation/_parent) . = ..() if(!istype(_parent)) diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index fa168fb5d3b29..819824239138e 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -203,6 +203,8 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) terminal = new /obj/machinery/power/terminal/invisible(T) terminal.master = src +CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/power/bsa/full) + /obj/machinery/power/bsa/full/Initialize(mapload, cannon_direction = WEST) . = ..() cell = new /obj/item/stock_parts/cell(src, 5000000) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 9efeb38665270..3e07f80683b95 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -377,6 +377,8 @@ var/obj/item/organ/eyes/robotic/glow/parent +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/abstract/eye_lighting) + /obj/effect/abstract/eye_lighting/Initialize(mapload, light_object_range, light_object_power, current_color_string, light_flags) . = ..() parent = loc diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_DEFINES/_unit_tests.dm similarity index 55% rename from code/modules/unit_tests/_unit_tests.dm rename to code/modules/unit_tests/_DEFINES/_unit_tests.dm index 89679c50c9ae9..15987f3ccf2bb 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_DEFINES/_unit_tests.dm @@ -51,10 +51,17 @@ #define UNIT_TEST_FAILED 1 #define UNIT_TEST_SKIPPED 2 +#define TEST_PRE 0 #define TEST_DEFAULT 1 /// After most test steps, used for tests that run long so shorter issues can be noticed faster #define TEST_LONGER 10 -#define TEST_DEL_WORLD INFINITY +/// This must be the one of last tests to run due to the inherent nature of the test iterating every single tangible atom in the game and qdeleting all of them (while taking long sleeps to make sure the garbage collector fires properly) taking a large amount of time. +#define TEST_CREATE_AND_DESTROY 9001 +/** + * For tests that rely on create and destroy having iterated through every (tangible) atom so they don't have to do something similar. + * Keep in mind tho that create and destroy will absolutely break the test platform, anything that relies on its shape cannot come after it. + */ +#define TEST_AFTER_CREATE_AND_DESTROY INFINITY /// Change color to red on ANSI terminal output, if enabled with -DANSICOLORS. #ifdef ANSICOLORS @@ -68,71 +75,77 @@ #else #define TEST_OUTPUT_GREEN(text) (text) #endif - +/// Change color to yellow on ANSI terminal output, if enabled with -DANSICOLORS. +#ifdef ANSICOLORS +#define TEST_OUTPUT_YELLOW(text) "\x1B\x5B1;33m[text]\x1B\x5B0m" +#else +#define TEST_OUTPUT_YELLOW(text) (text) +#endif /// A trait source when adding traits through unit tests #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" -#include "achievement_validation.dm" -#include "anchored_mobs.dm" -#include "antag_datums.dm" -#include "area_contents.dm" -#include "armour_checks.dm" -#include "asset_smart_cache.dm" -#include "async.dm" -#include "bloody_footprints.dm" -#include "check_adjustable_clothing.dm" -#include "closets.dm" -#include "component_tests.dm" -#include "connect_loc.dm" -#include "crafting_tests.dm" - -// Del the World. -// This unit test creates and qdels almost every atom in the code, checking for errors with initialization and harddels on deletion. -// It is disabled by default for now due to the large amount of consistent errors it produces. Run the "dm: find hard deletes" task to enable it. -#ifdef REFERENCE_TRACKING_FAST -#include "create_and_destroy.dm" -#endif - -#include "dcs_get_id_from_elements.dm" -#include "dynamic_ruleset_sanity.dm" -#include "enumerables.dm" -#include "gamemode_sanity.dm" -#include "keybinding_init.dm" -#include "rcd.dm" -#include "reagent_id_typos.dm" -#include "reagent_recipe_collisions.dm" -#include "siunit.dm" -#include "shuttle_width_height_correctness.dm" -#include "spawn_humans.dm" -#include "species_whitelists.dm" -#include "food_edibility_check.dm" -#include "greyscale_config.dm" -#include "heretic_knowledge.dm" -#include "heretic_rituals.dm" -#include "icon_smoothing_unit_test.dm" -#include "merge_type.dm" -#include "metabolizing.dm" -#include "missing_icons.dm" -#include "ntnetwork_tests.dm" -#include "preference_species.dm" -#include "projectiles.dm" -#include "stat_mc.dm" -#include "subsystem_init.dm" -#include "subsystem_metric_sanity.dm" -#include "surgery_linking.dm" -#include "techweb_sanity.dm" -#include "teleporters.dm" -#include "tgui_create_message.dm" -#include "timer_sanity.dm" -#include "unit_test.dm" -#include "random_ruin_mapsize.dm" -#include "walls_have_sheets.dm" -#include "worn_icons.dm" +#include "../achievement_validation.dm" +#include "../anchored_mobs.dm" +#include "../antag_datums.dm" +#include "../area_contents.dm" +#include "../armour_checks.dm" +#include "../asset_smart_cache.dm" +#include "../async.dm" +#include "../bloody_footprints.dm" +#include "../check_adjustable_clothing.dm" +#include "../closets.dm" +#include "../component_tests.dm" +#include "../connect_loc.dm" +#include "../crafting_tests.dm" +//#include "../create_and_destroy.dm" +#include "../dcs_get_id_from_elements.dm" +#include "../dynamic_ruleset_sanity.dm" +#include "../enumerables.dm" +#include "../gamemode_sanity.dm" +#include "../keybinding_init.dm" +#include "../rcd.dm" +#include "../reagent_id_typos.dm" +#include "../reagent_recipe_collisions.dm" +#include "../siunit.dm" +#include "../shuttle_width_height_correctness.dm" +#include "../spawn_humans.dm" +#include "../species_whitelists.dm" +#include "../food_edibility_check.dm" +#include "../greyscale_config.dm" +#include "../heretic_knowledge.dm" +#include "../heretic_rituals.dm" +#include "../icon_smoothing_unit_test.dm" +#include "../merge_type.dm" +#include "../metabolizing.dm" +#include "../missing_icons.dm" +#include "../ntnetwork_tests.dm" +#include "../preference_species.dm" +#include "../projectiles.dm" +#include "../stat_mc.dm" +#include "../subsystem_init.dm" +#include "../subsystem_metric_sanity.dm" +#include "../surgery_linking.dm" +#include "../techweb_sanity.dm" +#include "../teleporters.dm" +#include "../tgui_create_message.dm" +#include "../timer_sanity.dm" +#include "../unit_test.dm" +#include "../random_ruin_mapsize.dm" +#include "../walls_have_sheets.dm" +#include "../worn_icons.dm" +#include "../worn_icons.dm" #ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter -#include "find_reference_sanity.dm" +#include "../find_reference_sanity.dm" #endif +#include "../mapping/check_active_turfs.dm" +#include "../mapping/check_area_apc.dm" +#include "../mapping/check_camera_attachment.dm" +#include "../mapping/check_disposals.dm" +#include "../mapping/check_light_attachment.dm" +#include "../mapping/map_test.dm" + #undef TEST_ASSERT #undef TEST_ASSERT_EQUAL #undef TEST_ASSERT_NOTEQUAL diff --git a/code/modules/unit_tests/armour_checks.dm b/code/modules/unit_tests/armour_checks.dm index 2d49698d523d8..33f9af090589d 100644 --- a/code/modules/unit_tests/armour_checks.dm +++ b/code/modules/unit_tests/armour_checks.dm @@ -75,6 +75,8 @@ slot_flags = ALL body_parts_covered = ALL +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/clothing/suit/test_vest) + /obj/item/clothing/suit/test_vest/Initialize(mapload, armour_values) armor = armour_values . = ..() diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 2b78613e5753b..cf2cd51d27bd1 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -1,101 +1,29 @@ ///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_DEL_WORLD + //You absolutely must run after (almost) everything else + 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( - //this is somehow a subtype of /atom/movable, because of its purpose... - /image/appearance - //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, - //Template type - /obj/effect/mob_spawn, - //Say it with me now, type template - /obj/effect/mapping_helpers/component_injector, - //Singleton - /mob/dview, - //Xenobio basetypes - /obj/item/slimecross/gentle, - - ) - //This turf existing is an error in and of itself - ignore += typesof(/turf/baseturf_skipover) - ignore += typesof(/turf/baseturf_bottom) - ignore += typesof(/turf/template_noop) - //This one demands a computer, ditto - ignore += typesof(/obj/item/modular_computer/processor) - //Needs special input, let's be nice - ignore += typesof(/obj/effect/abstract/proximity_checker) - //Very finiky, blacklisting to make things easier - ignore += typesof(/obj/item/poster/wanted) - //We can't pass a mind into this - ignore += typesof(/obj/item/phylactery) - //This expects a seed, we can't pass it - ignore += typesof(/obj/item/food/grown) - //Nothing to hallucinate if there's nothing to hallicinate - ignore += typesof(/obj/effect/hallucination) - //Can't pass in a thing to glow - ignore += typesof(/obj/effect/abstract/eye_lighting) - //It wants a lot more context then we have - ignore += typesof(/obj/effect/buildmode_line) - //We don't have a pod - ignore += typesof(/obj/effect/pod_landingzone_effect) - ignore += typesof(/obj/effect/pod_landingzone) - //We don't have a disease to pass in - ignore += typesof(/obj/effect/mapping_helpers/component_injector/infective) - //There's no shapeshift to hold - ignore += typesof(/obj/shapeshift_holder) - //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 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/modules) - //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) - //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) - //Expects a mob to holderize, we have nothing to give - ignore += typesof(/obj/item/clothing/head/mob_holder) - //Expects a hud, generally created for player occupied mobs. There won't be any and this causes issues for us. - ignore += typesof(/atom/movable/plane_master_controller) var/list/cached_contents = spawn_at.contents.Copy() - var/baseturf_count = length(spawn_at.baseturfs) + 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) - for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please + GLOB.running_create_and_destroy = TRUE + for(var/type_path in typesof(/atom/movable, /turf) - uncreatables) //No areas please if(ispath(type_path, /turf)) - spawn_at.ChangeTurf(type_path, /turf/baseturf_skipover) - //We change it back to prevent pain, please don't ask - spawn_at.ChangeTurf(/turf/open/floor/wood, /turf/baseturf_skipover) - if(baseturf_count != length(spawn_at.baseturfs)) - TEST_FAIL("[type_path] changed the amount of baseturfs we have [baseturf_count] -> [length(spawn_at.baseturfs)]") - baseturf_count = length(spawn_at.baseturfs) + 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)) @@ -112,35 +40,49 @@ for(var/atom/to_kill in to_del) qdel(to_kill) - //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 + GLOB.running_create_and_destroy = FALSE + + // 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 //Clear it, just in case cached_contents.Cut() + var/list/queues_we_care_about = list() + // All of em, I want hard deletes too, since we rely on the debug info from them + for(var/i in 1 to GC_QUEUE_HARDDELETE) + queues_we_care_about += i + //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] + // + 2 seconds to ensure that everything gets in the queue. + var/time_needed = 2 SECONDS + for(var/index in queues_we_care_about) + time_needed += SSgarbage.collection_timeout[index] + var/start_time = world.time + var/real_start_time = REALTIMEOFDAY 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/oldest_packet_creation = INFINITY + for(var/index in queues_we_care_about) + var/list/queue_to_check = SSgarbage.queues[index] + if(!length(queue_to_check)) + continue + + var/list/oldest_packet = queue_to_check[1] + //Pull out the time we inserted at + var/qdeld_at = oldest_packet[GC_QUEUE_ITEM_GCD_DESTROYED] + + oldest_packet_creation = min(qdeld_at, oldest_packet_creation) - 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) + //That said, if there are any pending hard deletes you may NOT sleep, we gotta handle that shit + if(oldest_packet_creation > start_time && !length(SSgarbage.queues[GC_QUEUE_HARDDELETE])) 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 + if(REALTIMEOFDAY > real_start_time + time_needed + 50 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 @@ -159,6 +101,9 @@ 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]") + //if(LAZYLEN(item.extra_details)) + // var/details = item.extra_details.Join("\n") + // TEST_FAIL("[item.name] failed with extra info: \n[details]") cache_for_sonic_speed = SSatoms.BadInitializeCalls for(var/path in cache_for_sonic_speed) @@ -166,10 +111,9 @@ 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()") + TEST_FAIL("[path] qdel'd before we could call Initialize()") 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 + SSgarbage.collection_timeout[GC_QUEUE_CHECK] = GC_CHECK_QUEUE diff --git a/code/modules/unit_tests/mapping/check_active_turfs.dm b/code/modules/unit_tests/mapping/check_active_turfs.dm new file mode 100644 index 0000000000000..12a4d26641fa6 --- /dev/null +++ b/code/modules/unit_tests/mapping/check_active_turfs.dm @@ -0,0 +1,6 @@ +/datum/unit_test/map_test/active_turfs/check_map() + var/list/failures = list() + for(var/turf/t in GLOB.active_turfs_startlist) + failures += "Roundstart active turf at ([t.x], [t.y], [t.z] in [t.loc])" + if (length(failures)) + TEST_FAIL(jointext(failures, "\n")) diff --git a/code/modules/unit_tests/mapping/check_area_apc.dm b/code/modules/unit_tests/mapping/check_area_apc.dm new file mode 100644 index 0000000000000..2a9d7cd995d86 --- /dev/null +++ b/code/modules/unit_tests/mapping/check_area_apc.dm @@ -0,0 +1,10 @@ +/datum/unit_test/map_test/apc/check_area(area/check_area) + // Make sure there are no APCs in unpowered areas + if (check_area.apc && check_area.always_unpowered) + return "APC found in an always unpowered area" + // If you have power then I guess you pass + if (check_area.powered(AREA_USAGE_ENVIRON)) + return + // Otherwise, make sure we need power + if (!check_area.apc && !check_area.always_unpowered) + return "No APC in an area that requires power" diff --git a/code/modules/unit_tests/mapping/check_camera_attachment.dm b/code/modules/unit_tests/mapping/check_camera_attachment.dm new file mode 100644 index 0000000000000..a74a2ac3a52d9 --- /dev/null +++ b/code/modules/unit_tests/mapping/check_camera_attachment.dm @@ -0,0 +1,8 @@ +/datum/unit_test/map_test/camera/check_turf(turf/check_turf, is_map_border) + var/found = FALSE + for (var/obj/machinery/camera/camera in check_turf) + if (found) + return "Multiple cameras detected" + if (!isclosedturf(get_step(check_turf, camera.dir))) + return "Camera not attached to a wall" + found = TRUE diff --git a/code/modules/unit_tests/mapping/check_disposals.dm b/code/modules/unit_tests/mapping/check_disposals.dm new file mode 100644 index 0000000000000..de516887265a1 --- /dev/null +++ b/code/modules/unit_tests/mapping/check_disposals.dm @@ -0,0 +1,93 @@ +/obj/structure/disposalpipe/var/_traversed = 0 + +/datum/unit_test/map_test/check_disposals + var/failure_reason + var/is_sorting_network + var/run_id = 1 + +// Find all entries into the disposal system +/datum/unit_test/map_test/check_disposals/collect_targets(list/turfs) + var/located = list() + for (var/turf/check_turf in turfs) + var/found = locate(/obj/machinery/disposal) in check_turf + if (found) + located += found + return located + +// Make sure that we can end up in the correct location +/datum/unit_test/map_test/check_disposals/check_target(obj/machinery/disposal/target) + var/list/failures = list() + failure_reason = null + is_sorting_network = FALSE + if (!target.trunk) + return "[target.name] not attached to a trunk" + // Create a terrible disposal holder object + var/obj/structure/disposalholder/holder = new() + traverse_loop(holder, target.trunk, FALSE) + // Abuse byonds variables to get out (We can use pointers as an out variable in 515) + if (failure_reason) + failures += failure_reason + // This is fine, we probably are a bin that leads to space or something + if (!is_sorting_network) + return failures + holder.last_pipe = null + holder.current_pipe = null + failure_reason = null + // Since we have filters, lets make sure this is a proper, fully connected and fully functioning loop + // We should be able to enter the loop at any point from an input gate to get to our destination + var/i = 0 + for (var/sort_code in GLOB.TAGGERLOCATIONS) + i++ + holder.destinationTag = i + holder.unsorted = TRUE + var/atom/destination = traverse_loop(holder, target.trunk, TRUE) + if (failure_reason) + failures += failure_reason + continue + var/arrived = FALSE + for (var/valid_destination in GLOB.tagger_destination_areas[sort_code]) + if (istype(get_area(destination), valid_destination)) + arrived = TRUE + break + if (!arrived) + failures += "Disposal track starting at [COORD(target)] does not end up in the correct destination. Expected [sort_code] ([i]), got [get_area(destination)] at [COORD(destination)]" + return failures + +/datum/unit_test/map_test/check_disposals/proc/traverse_loop(obj/structure/disposalholder/holder, obj/structure/disposalpipe/start, allow_inputs) + // Increment run ID + run_id++ + // First check to ensure that we end up somewhere + var/obj/structure/disposalpipe/current = start + holder.current_pipe = current + holder.dir = current.dir || SOUTH + var/has_looped = FALSE + while (current) + // Account for disposals shitcode + holder.dir = istype(current, /obj/structure/disposalpipe/trunk) ? (current.dir || SOUTH) : current.nextdir(holder) + var/turf/T = get_step(current, holder.dir) + current = holder.findpipe(T) + // End detection + if (current == null) + failure_reason = "Disposal network starting at [COORD(start)] has a pipe with no output at [COORD(T)] but should lead to an outlet. Holder was traversing [dir2text(holder.dir)] and was last at [COORD(holder.current_pipe)]. Sort code was [holder.destinationTag]." + return + holder.last_pipe = holder.current_pipe + holder.current_pipe = current + // If we have re-entered the loop at the unsorting pip, increment run ID as we will have a different behaviour next time we loop around + if (!has_looped && !holder.unsorted) + run_id ++ + has_looped = TRUE + // Found a valid ending + if (locate(/obj/structure/disposaloutlet) in T) + return locate(/obj/structure/disposaloutlet) + // Detect ending back at an input + if (locate(/obj/machinery/disposal) in T) + if (!allow_inputs) + failure_reason = "Disposal loop starting at [COORD(start)] leads to an input node at [COORD(T)] but should lead to an outlet. Holder was traversing [dir2text(holder.dir)] and was last at [COORD(holder.last_pipe)]. Sort code was [holder.destinationTag]." + return current + if (locate(/obj/structure/disposalpipe/sorting) in T) + is_sorting_network = TRUE + // Loop detection + if (current._traversed == run_id) + failure_reason = "Disposal network starting at [COORD(start)] contains a loop at [COORD(T)] which is not allowed. Holder was traversing [dir2text(holder.dir)] and was last at [COORD(holder.last_pipe)]. Sort code was [holder.destinationTag]." + return + current._traversed = run_id diff --git a/code/modules/unit_tests/mapping/check_light_attachment.dm b/code/modules/unit_tests/mapping/check_light_attachment.dm new file mode 100644 index 0000000000000..ec22c1844be10 --- /dev/null +++ b/code/modules/unit_tests/mapping/check_light_attachment.dm @@ -0,0 +1,10 @@ +/datum/unit_test/map_test/lights/check_turf(turf/check_turf, is_map_border) + var/found = FALSE + for (var/obj/machinery/light/light in check_turf) + if (istype(light, /obj/machinery/light/floor)) + continue + if (found) + return "Multiple lights detected" + if (!isclosedturf(get_step(check_turf, light.dir))) + return "Light not attached to a wall" + found = TRUE diff --git a/code/modules/unit_tests/mapping/map_test.dm b/code/modules/unit_tests/mapping/map_test.dm new file mode 100644 index 0000000000000..c80b23eb1d912 --- /dev/null +++ b/code/modules/unit_tests/mapping/map_test.dm @@ -0,0 +1,60 @@ +/datum/unit_test/map_test/Run() + var/list/failures + var/list/areas = list() + var/list/turfs = list() + // Check turfs + for (var/z in 1 to world.maxz) + if (!is_station_level(z)) + continue + for (var/x in 1 to world.maxx) + for (var/y in 1 to world.maxy) + var/turf/tile = locate(x, y, z) + turfs += tile + areas[tile.loc] = TRUE + var/result = check_turf(tile, x == 1 || x == world.maxx || y == 1 || y == world.maxy) + if (result) + LAZYADD(failures, "([x], [y], [z]): [result]") + // Check areas + for (var/area/A in areas) + var/result = check_area(A) + if (result) + LAZYADD(failures, "([A.type]): [result]") + // Check Zs + for (var/z in 1 to world.maxz) + if (!is_station_level(z)) + continue + var/result = check_z_level(z) + if (result) + LAZYADD(failures, result) + // Get things we want to specifically test for + var/list/targets = collect_targets(turfs) + for (var/target in targets) + var/result = check_target(target) + if (result) + LAZYADD(failures, result) + // Full map general checks + var/result = check_map() + if (result) + LAZYADD(failures, result) + // Fail if necessary + for (var/failure in failures) + TEST_FAIL(failure) + +/// Return a string if failed, return null otherwise +/datum/unit_test/map_test/proc/check_turf(turf/check_turf, is_map_border) + +/// Return a string if failed, return null otherwise +/datum/unit_test/map_test/proc/check_area(area/check_area) + +/// Return a string if failed, return null otherwise +/datum/unit_test/map_test/proc/check_z_level(z_value) + +/// Return a string if failed, return null otherwise +/datum/unit_test/map_test/proc/check_map() + +/// Returns a list of things that you want to specifically check +/datum/unit_test/map_test/proc/collect_targets(list/turfs) + return list() + +/// Return a string if failed, return null otherwise +/datum/unit_test/map_test/proc/check_target(atom/target) diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 69f3208cdb29a..556bdf7df6ac2 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -13,7 +13,22 @@ You can use the run_loc_floor_bottom_left and run_loc_floor_top_right to get tur GLOBAL_DATUM(current_test, /datum/unit_test) GLOBAL_VAR_INIT(failed_any_test, FALSE) -GLOBAL_VAR(test_log) +/// When unit testing, all logs sent to log_mapping are stored here and retrieved in log_mapping unit test. +GLOBAL_LIST_EMPTY(unit_test_mapping_logs) +/// Global assoc list of required mapping items, [item typepath] to [required item datum]. +GLOBAL_LIST_EMPTY(required_map_items) + +/// A list of every test that is currently focused. +/// Use the PERFORM_ALL_TESTS macro instead. +GLOBAL_VAR_INIT(focused_tests, focused_tests()) + +/proc/focused_tests() + var/list/focused_tests = list() + for (var/datum/unit_test/unit_test as anything in subtypesof(/datum/unit_test) - /datum/unit_test/map_test) + if (initial(unit_test.focus)) + focused_tests += unit_test + + return focused_tests.len > 0 ? focused_tests : null /datum/unit_test //Bit of metadata for the future maybe @@ -32,6 +47,12 @@ GLOBAL_VAR(test_log) var/list/allocated var/list/fail_reasons + /// Do not instantiate if type matches this + var/abstract_type = /datum/unit_test + + /// List of atoms that we don't want to ever initialize in an agnostic context, like for Create and Destroy. Stored on the base datum for usability in other relevant tests that need this data. + var/static/list/uncreatables = null + var/static/datum/space_level/reservation /proc/cmp_unit_test_priority(datum/unit_test/a, datum/unit_test/b) @@ -42,6 +63,9 @@ GLOBAL_VAR(test_log) var/datum/map_template/unit_tests/template = new reservation = template.load_new_z() + if (isnull(uncreatables)) + uncreatables = build_list_of_uncreatables() + allocated = new run_loc_floor_bottom_left = get_turf(locate(/obj/effect/landmark/unit_test_bottom_left) in GLOB.landmarks_list) run_loc_floor_top_right = get_turf(locate(/obj/effect/landmark/unit_test_top_right) in GLOB.landmarks_list) @@ -52,7 +76,7 @@ GLOBAL_VAR(test_log) /datum/unit_test/Destroy() QDEL_LIST(allocated) // clear the test area - for (var/turf/turf in block(locate(1, 1, run_loc_floor_bottom_left.z), locate(world.maxx, world.maxy, run_loc_floor_bottom_left.z))) + for (var/turf/turf in Z_TURFS(run_loc_floor_bottom_left.z)) for (var/content in turf.contents) if (istype(content, /obj/effect/landmark)) continue @@ -60,7 +84,7 @@ GLOBAL_VAR(test_log) return ..() /datum/unit_test/proc/Run() - TEST_FAIL("Run() called parent or not implemented") + TEST_FAIL("[type]/Run() called parent or not implemented") /datum/unit_test/proc/Fail(reason = "No reason", file = "OUTDATED_TEST", line = 1) succeeded = FALSE @@ -74,14 +98,61 @@ GLOBAL_VAR(test_log) /// Instances allocated through this proc will be destroyed when the test is over /datum/unit_test/proc/allocate(type, ...) var/list/arguments = args.Copy(2) - if (!arguments.len) - arguments = list(run_loc_floor_bottom_left) - else if (arguments[1] == null) - arguments[1] = run_loc_floor_bottom_left - var/instance = new type(arglist(arguments)) + if(ispath(type, /atom)) + if (!arguments.len) + arguments = list(run_loc_floor_bottom_left) + else if (arguments[1] == null) + arguments[1] = run_loc_floor_bottom_left + var/instance + // Byond will throw an index out of bounds if arguments is empty in that arglist call. Sigh + if(length(arguments)) + instance = new type(arglist(arguments)) + else + instance = new type() allocated += instance return instance +/// Resets the air of our testing room to its default +/datum/unit_test/proc/restore_atmos() + var/area/working_area = run_loc_floor_bottom_left.loc + var/list/turf/to_restore = working_area.get_contained_turfs() + for(var/turf/open/restore in to_restore) + restore.Initalize_Atmos() + +/datum/unit_test/proc/test_screenshot(name, icon/icon) + if (!istype(icon)) + TEST_FAIL("[icon] is not an icon.") + return + + var/path_prefix = replacetext(replacetext("[type]", "/datum/unit_test/", ""), "/", "_") + name = replacetext(name, "/", "_") + + var/filename = "code/modules/unit_tests/screenshots/[path_prefix]_[name].png" + + if (fexists(filename)) + var/data_filename = "data/screenshots/[path_prefix]_[name].png" + fcopy(icon, data_filename) + log_test("\t[path_prefix]_[name] was found, putting in data/screenshots") + else if (fexists("code")) + // We are probably running in a local build + fcopy(icon, filename) + TEST_FAIL("Screenshot for [name] did not exist. One has been created.") + else + // We are probably running in real CI, so just pretend it worked and move on + fcopy(icon, "data/screenshots_new/[path_prefix]_[name].png") + + log_test("\t[path_prefix]_[name] was put in data/screenshots_new") + +/// Helper for screenshot tests to take an image of an atom from all directions and insert it into one icon +/datum/unit_test/proc/get_flat_icon_for_all_directions(atom/thing, no_anim = TRUE) + var/icon/output = icon('icons/effects/effects.dmi', "nothing") + + for (var/direction in GLOB.cardinals) + var/icon/partial = getFlatIcon(thing, defdir = direction, no_anim = no_anim) + output.Insert(partial, dir = direction) + + return output + /// Logs a test message. Will use GitHub action syntax found at https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions /datum/unit_test/proc/log_for_test(text, priority, file, line) var/map_name = SSmapping.config.map_name @@ -92,49 +163,74 @@ GLOBAL_VAR(test_log) log_world("::[priority] file=[file],line=[line],title=[map_name]: [type]::[annotation_text]") -/proc/RunUnitTest(test_path, list/test_results) +/proc/RunUnitTest(datum/unit_test/test_path, list/test_results) + if(initial(test_path.abstract_type) == test_path) + return var/datum/unit_test/test = new test_path GLOB.current_test = test var/duration = REALTIMEOFDAY + var/skip_test = (test_path in SSmapping.config.skipped_tests) + var/test_output_desc = "[test_path]" + var/message = "" log_world("::group::[test_path]") - test.Run() - duration = REALTIMEOFDAY - duration - GLOB.current_test = null - GLOB.failed_any_test |= !test.succeeded + if(skip_test) + log_world("[TEST_OUTPUT_YELLOW("SKIPPED")] Skipped run on map [SSmapping.config.map_name].") + + else + + test.Run() + test.restore_atmos() - var/list/log_entry = list() - var/list/fail_reasons = test.fail_reasons + duration = REALTIMEOFDAY - duration + GLOB.current_test = null + GLOB.failed_any_test |= !test.succeeded - for(var/reasonID in 1 to LAZYLEN(fail_reasons)) - var/text = fail_reasons[reasonID][1] - var/file = fail_reasons[reasonID][2] - var/line = fail_reasons[reasonID][3] + var/list/log_entry = list() + var/list/fail_reasons = test.fail_reasons - test.log_for_test(text, "error", file, line) + for(var/reasonID in 1 to LAZYLEN(fail_reasons)) + var/text = fail_reasons[reasonID][1] + var/file = fail_reasons[reasonID][2] + var/line = fail_reasons[reasonID][3] - // Normal log message - log_entry += "\tFAILURE #[reasonID]: [text] at [file]:[line]" + test.log_for_test(text, "error", file, line) - var/message = log_entry.Join("\n") - log_test(message) + // Normal log message + log_entry += "\tFAILURE #[reasonID]: [text] at [file]:[line]" - var/test_output_desc = "[test_path] [duration / 10]s" - if (test.succeeded) - log_world("[TEST_OUTPUT_GREEN("PASS")] [test_output_desc]") + if(length(log_entry)) + message = log_entry.Join("\n") + log_test(message) + + test_output_desc += " [duration / 10]s" + if (test.succeeded) + log_world("[TEST_OUTPUT_GREEN("PASS")] [test_output_desc]") log_world("::endgroup::") - if (!test.succeeded) + if (!test.succeeded && !skip_test) log_world("::error::[TEST_OUTPUT_RED("FAIL")] [test_output_desc]") - test_results[test_path] = list("status" = test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED, "message" = message, "name" = test_path) + var/final_status = skip_test ? UNIT_TEST_SKIPPED : (test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED) + test_results[test_path] = list("status" = final_status, "message" = message, "name" = test_path) qdel(test) +/* + +SEE test_helpers.dm for the redefinition of this proc. +We use a modular approach to adding things to the ignore list, but unfortunately +that means the proc needs to be defined prior to everything else. + +/datum/unit_test/proc/build_list_of_uncreatables() + RETURN_TYPE(/list) + return list() +*/ + /proc/RunUnitTests() CHECK_TICK @@ -147,26 +243,24 @@ GLOBAL_VAR(test_log) if(length(focused_tests)) tests_to_run = focused_tests - tests_to_run = sortTim(tests_to_run, GLOBAL_PROC_REF(cmp_unit_test_priority)) + sortTim(tests_to_run, GLOBAL_PROC_REF(cmp_unit_test_priority)) var/list/test_results = list() + //Hell code, we're bound to end the round somehow so let's stop if from ending while we work + SSticker.delay_end = TRUE for(var/unit_path in tests_to_run) CHECK_TICK //We check tick first because the unit test we run last may be so expensive that checking tick will lock up this loop forever RunUnitTest(unit_path, test_results) + SSticker.delay_end = FALSE var/file_name = "data/unit_tests.json" fdel(file_name) file(file_name) << json_encode(test_results) SSticker.force_ending = TRUE - //Comment from tgstation: We have to call this manually because del_text can preceed us, and SSticker doesn't fire in the post game - //We don't actually need to call standard_reboot, but leaving it under a condition in case it becomes necessary in the future. - //To my understanding, something triggers a reboot when it's created/deleted, which could interrupt the tests and restart the server. - //To prevent this, create_and_destroy prevents the reboot from happening. However, this also prevents the reboot from ever happening naturally. - //Because of this, in case something does actually attempt to reboot prematurely, we need to manually initiate the reboot. - if(SSticker.ready_for_reboot) - SSticker.standard_reboot() + //We have to call this manually because del_text can preceed us, and SSticker doesn't fire in the post game + SSticker.declare_completion() /datum/map_template/unit_tests name = "Unit Tests Zone" diff --git a/code/modules/uplink/uplink_devices.dm b/code/modules/uplink/uplink_devices.dm index 844384f0ee73b..9938435d80b6f 100644 --- a/code/modules/uplink/uplink_devices.dm +++ b/code/modules/uplink/uplink_devices.dm @@ -22,6 +22,8 @@ var/uplink_flag = UPLINK_TRAITORS +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/uplink) + /obj/item/uplink/Initialize(mapload, owner, tc_amount = 20) . = ..() AddComponent(/datum/component/uplink, owner, FALSE, TRUE, uplink_flag, tc_amount) @@ -29,6 +31,8 @@ /obj/item/uplink/debug name = "debug uplink" +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/uplink/debug) + /obj/item/uplink/debug/Initialize(mapload, owner, tc_amount = 9000) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) @@ -42,6 +46,8 @@ name = "debug nuclear uplink" uplink_flag = UPLINK_NUKE_OPS +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/uplink/nuclear/debug) + /obj/item/uplink/nuclear/debug/Initialize(mapload, owner, tc_amount = 9000) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) @@ -63,17 +69,23 @@ name = "dusty radio" desc = "A dusty looking radio." +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/uplink/old) + /obj/item/uplink/old/Initialize(mapload, owner, tc_amount = 10) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) hidden_uplink.name = "dusty radio" // Multitool uplink +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/multitool/uplink) + /obj/item/multitool/uplink/Initialize(mapload, owner, tc_amount = 20) . = ..() AddComponent(/datum/component/uplink, owner, FALSE, TRUE, UPLINK_TRAITORS, tc_amount) // Pen uplink +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/pen/uplink) + /obj/item/pen/uplink/Initialize(mapload, owner, tc_amount = 20) . = ..() AddComponent(/datum/component/uplink, owner, TRUE, FALSE, UPLINK_TRAITORS, tc_amount) diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm index e436afcaab9ee..242a4be15d74c 100644 --- a/code/modules/vehicles/mecha/combat/durand.dm +++ b/code/modules/vehicles/mecha/combat/durand.dm @@ -153,6 +153,8 @@ own integrity back to max. Shield is automatically dropped if we run out of powe var/currentuser +CREATION_TEST_IGNORE_SUBTYPES(/obj/durand_shield) + /obj/durand_shield/Initialize(mapload, _chassis, _layer, _dir) . = ..() chassis = _chassis diff --git a/code/modules/vehicles/mecha/mecha_wreckage.dm b/code/modules/vehicles/mecha/mecha_wreckage.dm index 530c34a5a378a..e0f584c708dab 100644 --- a/code/modules/vehicles/mecha/mecha_wreckage.dm +++ b/code/modules/vehicles/mecha/mecha_wreckage.dm @@ -17,6 +17,8 @@ var/mob/living/silicon/ai/AI //AIs to be salvaged var/list/parts +CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/mecha_wreckage) + /obj/structure/mecha_wreckage/Initialize(mapload, mob/living/silicon/ai/AI_pilot) . = ..() if(parts) diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index 65bc8a9ffcbf6..0100a3053b6b1 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -49,6 +49,8 @@ /// A reference to the action button itself var/datum/action/innate/bci_action/bci_action +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/circuit_component/bci_action) + /obj/item/circuit_component/bci_action/Initialize(mapload, default_icon) . = ..() diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm index 72e6e12c13f01..51e0f600497e7 100644 --- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm +++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm @@ -175,6 +175,8 @@ action_background_icon_state = "bg_spell" var/obj/item/xenoartifact/xeno +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/proc_holder/spell/targeted/xeno_senitent_action) + /obj/effect/proc_holder/spell/targeted/xeno_senitent_action/Initialize(mapload, var/obj/item/xenoartifact/Z) . = ..() xeno = Z @@ -203,6 +205,8 @@ invisibility = 101 var/obj/item/xenoartifact/artifact +CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/mob_spawn/sentient_artifact) + /obj/effect/mob_spawn/sentient_artifact/Initialize(mapload, var/obj/item/xenoartifact/Z) if(!Z) qdel(src) diff --git a/code/modules/xenoarchaeology/xenoartifact.dm b/code/modules/xenoarchaeology/xenoartifact.dm index c1d8dcf9da7ab..9dfb1ec733e07 100644 --- a/code/modules/xenoarchaeology/xenoartifact.dm +++ b/code/modules/xenoarchaeology/xenoartifact.dm @@ -58,6 +58,8 @@ AddComponent(/datum/component/xenoartifact_pricing) AddComponent(/datum/component/discoverable, XENOA_DP, TRUE) //Same values as original artifacts from exploration +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/xenoartifact) + /obj/item/xenoartifact/Initialize(mapload, difficulty) . = ..() @@ -422,6 +424,8 @@ /obj/item/xenoartifact/maint //Semi-toddler-safe version, for maint loot table. material = XENOA_BLUESPACE +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/xenoartifact/maint) + /obj/item/xenoartifact/maint/Initialize(mapload, difficulty) if(prob(1)) material = pick(XENOA_PLASMA, XENOA_URANIUM, XENOA_BANANIUM) @@ -447,6 +451,8 @@ price = f_price ///Objective version for exploration +CREATION_TEST_IGNORE_SUBTYPES(/obj/item/xenoartifact/objective) + /obj/item/xenoartifact/objective/Initialize(mapload, difficulty) traits += new /datum/xenoartifact_trait/special/objective . = ..() diff --git a/tools/ci/annotate_dm.sh b/tools/ci/annotate_dm.sh new file mode 100644 index 0000000000000..e43f930ba1acc --- /dev/null +++ b/tools/ci/annotate_dm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +set -euo pipefail +tools/bootstrap/python -m dm_annotator "$@" diff --git a/tools/ci/annotate_od.sh b/tools/ci/annotate_od.sh new file mode 100644 index 0000000000000..12390908074c0 --- /dev/null +++ b/tools/ci/annotate_od.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +set -euo pipefail +tools/bootstrap/python -m od_annotator "$@" diff --git a/tools/ci/check_grep.sh b/tools/ci/check_grep.sh index efc665bfb5f2a..d8ce9ea3dc96e 100755 --- a/tools/ci/check_grep.sh +++ b/tools/ci/check_grep.sh @@ -2,7 +2,7 @@ set -euo pipefail #nb: must be bash to support shopt globstar -shopt -s globstar +shopt -s globstar extglob #ANSI Escape Codes for colors to increase contrast of errors RED="\033[0;31m" @@ -10,18 +10,89 @@ GREEN="\033[0;32m" BLUE="\033[0;34m" NC="\033[0m" # No Color -# Copy-pasted text -HINT_REMOVE="please remove them. (Hint: Find out which area they are in!)${NC}" - st=0 -echo -e "${BLUE}Checking for map issues...${NC}" +# check for ripgrep +if command -v rg >/dev/null 2>&1; then + grep=rg + pcre2_support=1 + if [ ! rg -P '' >/dev/null 2>&1 ] ; then + pcre2_support=0 + fi + code_files="code/**/**.dm" + map_files="_maps/**/**.dmm" + shuttle_map_files="_maps/shuttles/**.dmm" + code_x_515="code/**/!(__byond_version_compat).dm" +else + pcre2_support=0 + grep=grep + code_files="-r --include=code/**/**.dm" + map_files="-r --include=_maps/**/**.dmm" + shuttle_map_files="-r --include=_maps/shuttles/**.dmm" + code_x_515="-r --include=code/**/!(__byond_version_compat).dm" +fi -if grep -El '^\".+\" = \(.+\)' _maps/**/*.dmm; then - echo +echo -e "${BLUE}Using grep provider at $(which $grep)${NC}" + +part=0 +section() { + echo -e "${BLUE}Checking for $1${NC}..." + part=0 +} + +part() { + part=$((part+1)) + padded=$(printf "%02d" $part) + echo -e "${GREEN} $padded- $1${NC}" +} + +section "map issues" + +part "TGM" +if $grep -U '^".+" = \(.+\)' $map_files; then + echo echo -e "${RED}ERROR: Non-TGM formatted map detected. Please convert it using Map Merger!${NC}" st=1 fi; +part "comments" +if $grep '//' $map_files | $grep -v '//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE' | $grep -v 'name|desc'; then + echo + echo -e "${RED}ERROR: Unexpected commented out line detected in this map file. Please remove it.${NC}" + st=1 +fi; +part "iconstate tags" +if $grep '^\ttag = "icon' $map_files; then + echo + echo -e "${RED}ERROR: Tag vars from icon state generation detected in maps, please remove them.${NC}" + st=1 +fi; +part "invalid map procs" +if $grep '(new|newlist|icon|matrix|sound)\(.+\)' $map_files; then + echo + echo -e "${RED}ERROR: Using unsupported procs in variables in a map file! Please remove all instances of this.${NC}" + st=1 +fi; +part "common spelling mistakes" +if $grep -i 'nanotransen' $map_files; then + echo + echo -e "${RED}ERROR: Misspelling(s) of Nanotrasen detected in maps, please remove the extra N(s).${NC}" + st=1 +fi; +if $grep 'NanoTrasen' $map_files; then + echo + echo -e "${RED}ERROR: Misspelling(s) of Nanotrasen detected in maps, please uncapitalize the T(s).${NC}" + st=1 +fi; +if $grep -i'centcomm' $map_files; then + echo + echo -e "${RED}ERROR: Misspelling(s) of CentCom detected in maps, please remove the extra M(s).${NC}" + st=1 +fi; +if $grep -i'eciev' $map_files; then + echo + echo -e "${RED}ERROR: Common I-before-E typo detected in maps.${NC}" + st=1 +fi; if grep -P 'Merge Conflict Marker' _maps/**/*.dmm; then echo "ERROR: Merge conflict markers detected in map, please resolve all merge failures!" st=1 @@ -31,11 +102,6 @@ if grep -P '/obj/merge_conflict_marker' _maps/**/*.dmm; then echo "ERROR: Merge conflict markers detected in map, please resolve all merge failures!" st=1 fi; -if grep -P '^\ttag = \"icon' _maps/**/*.dmm; then - echo - echo -e "${RED}ERROR: Tag vars from icon state generation detected in maps, ${HINT_REMOVE}" - st=1 -fi; if grep -P 'step_[xy]' _maps/**/*.dmm; then echo echo -e "${RED}ERROR: step_x/step_y variables detected in maps, ${HINT_REMOVE}" @@ -51,7 +117,6 @@ if grep -P '\td[1-2] =' _maps/**/*.dmm; then echo -e "${RED}ERROR: d1/d2 cable variables detected in maps, ${HINT_REMOVE}" st=1 fi; -echo -e "${BLUE}Checking duplicate structures...${NC}" if grep -Pzo '"\w+" = \([^)]*?\n/obj/effect/mapping_helpers/simple_pipes(?[/\w]*),[^)]*?\n/obj/effect/mapping_helpers/simple_pipes\g{type},[^)]*?\n/area/.+\)' _maps/**/*.dmm; then echo echo -e "${RED}ERROR: Found multiple idendical simple_pipes mapping helpers on the same tile, ${HINT_REMOVE}" @@ -123,26 +188,6 @@ if grep -P '\W\/turf\s*[,\){]' _maps/**/*.dmm; then echo -e "${RED}ERROR: Base /turf path use detected in maps, please replace a with proper turf path.${NC}" st=1 fi; -if grep -P '^/*var/' code/**/*.dm; then - echo - echo -e "${RED}ERROR: Unmanaged global var use detected in code, please use the helpers.${NC}" - st=1 -fi; -if grep -i 'centcomm' code/**/*.dm; then - echo - echo -e "${RED}ERROR: Misspelling(s) of CentCom detected in code, please remove the extra M(s).${NC}" - st=1 -fi; -if grep -i 'centcomm' _maps/**/*.dm; then - echo - echo -e "${RED}ERROR: Misspelling(s) of CentCom detected in maps, please remove the extra M(s).${NC}" - st=1 -fi; -if grep -P 'set name\s*=\s*"[\S\s]*![\S\s]*"' code/**/*.dm; then - echo - echo -e "${RED}ERROR: Verb with name containing an exclamation point found. These verbs are not compatible with TGUI chat's statpanel or chat box.${NC}" - st=1 -fi; if grep -Pzo '"\w+" = \([^)]*?\n/turf/[/\w,\n]*?[^)]*?\n/turf/[/\w,\n]*?[^)]*?\n/area/.+?\)' _maps/**/*.dmm; then echo echo -e "${RED}ERROR: Multiple turfs detected on the same tile! Please choose only one turf!${NC}" @@ -184,61 +229,210 @@ if grep -Pzo '/obj/machinery/conveyor/inverted[/\w]*?\{\n[^}]*?dir = [1248];[^}] echo -e "${RED}ERROR: Found an inverted conveyor belt with a cardinal dir. Please replace it with a normal conveyor belt.${NC}" st=1 fi; -echo -e "${BLUE}Checking mapping JSON...${NC}" -if ls _maps/*.json | grep -P "[A-Z]"; then + +section "whitespace issues" +part "space indentation" +if $grep '(^ {2})|(^ [^ * ])|(^ +)' $code_files; then + echo + echo -e "${RED}ERROR: Space indentation detected, please use tab indentation.${NC}" + st=1 +fi; +part "mixed indentation" +if $grep '^\t+ [^ *]' $code_files; then + echo + echo -e "${RED}ERROR: Mixed indentation detected, please stick to tab indentation.${NC}" + st=1 +fi; + +section "unit tests" +unit_test_files="code/modules/unit_tests/**/**.dm" +part "mob/living/carbon/human usage" +if $grep 'allocate\(/mob/living/carbon/human[,\)]' $unit_test_files || + $grep 'new /mob/living/carbon/human\s?\(' $unit_test_files || + $grep 'var/mob/living/carbon/human/\w+\s?=\s?new' $unit_test_files ; then + echo + echo -e "${RED}ERROR: Usage of mob/living/carbon/human detected in a unit test, please use mob/living/carbon/human/consistent.${NC}" + st=1 +fi; + +section "common mistakes" +part "global vars" +if $grep '^/*var/' $code_files; then + echo + echo -e "${RED}ERROR: Unmanaged global var use detected in code, please use the helpers.${NC}" + st=1 +fi; + +part "can_perform_action argument check" +if $grep 'can_perform_action\(\s*\)' $code_files; then + echo + echo -e "${RED}ERROR: Found a can_perform_action() proc with improper arguments.${NC}" + st=1 +fi; + +part "src as a trait source" # ideally we'd lint / test for ANY datum reference as a trait source, but 'src' is the most common. +if $grep -i '(add_trait|remove_trait)\(.+,\s*.+,\s*src\)' $code_files; then + echo + echo -e "${RED}ERROR: Using 'src' as a trait source. Source must be a string key - dont't use references to datums as a source, perhaps use 'REF(src)'.${NC}" + st=1 +fi; +if $grep -i '(add_traits|remove_traits)\(.+,\s*src\)' $code_files; then + echo + echo -e "${RED}ERROR: Using 'src' as trait sources. Source must be a string key - dont't use references to datums as sources, perhaps use 'REF(src)'.${NC}" + st=1 +fi; + +part "ensure proper lowertext usage" +# lowertext() is a BYOND-level proc, so it can be used in any sort of code... including the TGS DMAPI which we don't manage in this repository. +# basically, we filter out any results with "tgs" in it to account for this edgecase without having to enforce this rule in that separate codebase. +# grepping the grep results is a bit of a sad solution to this but it's pretty much the only option in our existing linter framework +if $grep -i 'lowertext\(.+\)' $code_files | $grep -v 'UNLINT\(.+\)' | $grep -v '\/modules\/tgs\/'; then + echo + echo -e "${RED}ERROR: Found a lowertext() proc call. Please use the LOWER_TEXT() macro instead. If you know what you are doing, wrap your text (ensure it is a string) in UNLINT().${NC}" + st=1 +fi; + +part "balloon_alert sanity" +if $grep 'balloon_alert\(".*"\)' $code_files; then + echo + echo -e "${RED}ERROR: Found a balloon alert with improper arguments.${NC}" + st=1 +fi; + +if $grep 'balloon_alert(.*.*?), line (?P\d+), column (?P\d+):\s{1,2}(?Perror|warning): (?P.*)' + has_issues = False + + print("DM Code Annotations:") + for annotation in re.finditer(annotation_regex, raw_output): + print(f"::{annotation['type']} file={annotation['filename']},line={annotation['line']},col={annotation['column']}::{annotation['message']}") + has_issues = True + + if not has_issues: + print(green("No DM issues found")) + +def main(): + if len(sys.argv) > 1: + if not path.exists(sys.argv[1]): + print(red(f"Error: Annotations file '{sys.argv[1]}' does not exist")) + sys.exit(1) + with open(sys.argv[1], 'r') as f: + annotate(f.read()) + elif not sys.stdin.isatty(): + annotate(sys.stdin.read()) + else: + print(red("Error: No input provided")) + print("Usage: tools/bootstrap/python -m dm_annotator [filename]") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/tools/expand_filedir_paths.py b/tools/expand_filedir_paths.py index b58a8679398c5..db0c682562fc5 100644 --- a/tools/expand_filedir_paths.py +++ b/tools/expand_filedir_paths.py @@ -20,7 +20,7 @@ def read_filedirs(filename): result = [] dme_file = file(filename, "rt") - + # Read each line from the file and check for regex pattern match for row in dme_file: match = filedir_pattern.match(row) @@ -63,7 +63,7 @@ def replace_func(name): else: replacement = name.group(1) return "'" + replacement + "'" - + # Search recursively for all .dm and .dmm files for (dirpath, dirs, files) in os.walk("."): for name in files: @@ -88,6 +88,6 @@ def replace_func(name): os.remove(path) os.rename(path + ".tmp", path) -dirs = read_filedirs("tgstation.dme"); +dirs = read_filedirs("beestation.dme"); resources = index_files(dirs) rewrite_sources(resources) diff --git a/tools/od_annotator/__main__.py b/tools/od_annotator/__main__.py new file mode 100644 index 0000000000000..357adccfe913c --- /dev/null +++ b/tools/od_annotator/__main__.py @@ -0,0 +1,50 @@ +import sys +import re + +def green(text): + return "\033[32m" + str(text) + "\033[0m" + +def red(text): + return "\033[31m" + str(text) + "\033[0m" + +def annotate(raw_output): + # Remove ANSI escape codes + raw_output = re.sub(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]', '', raw_output) + + print("::group::OpenDream Output") + print(raw_output) + print("::endgroup::") + + annotation_regex = r'((?PError|Warning) (?POD(?P\d{4})) at (?P(?P.+):(?P\d+):(?P\d+)|): (?P.+))' + failures_detected = False + expected_failure_case_detected = False # this is just here so this script breaks if we forget to set it to True when we expect a failure. remove this when we have handled the expected failure + + print("OpenDream Code Annotations:") + for annotation in re.finditer(annotation_regex, raw_output): + message = annotation['message'] + if message == "Unimplemented proc & var warnings are currently suppressed": # this happens every single run, it's important to know about it but we don't need to throw an error + message += " (This is expected and can be ignored)" # also there's no location for it to annotate to since it's an failure. + expected_failure_case_detected = True + + if annotation['type'] == "Error": + failures_detected = True + + error_string = f"{annotation['errorcode']}: {message}" + + if annotation['location'] == "": + print(f"::{annotation['type']} file=,line=,col=::{error_string}") + else: + print(f"::{annotation['type']} file={annotation['filename']},line={annotation['line']},col={annotation['column']}::{error_string}") + + if failures_detected: + sys.exit(1) + return + + if not expected_failure_case_detected: + print(red("Failed to detect the expected failure case! If you have recently changed how we work with OpenDream Pragmas, please fix the od_annotator script!")) + sys.exit(1) + return + + print(green("No OpenDream issues found!")) + +annotate(sys.stdin.read()) diff --git a/tools/ticked_file_enforcement/schemas/beestation_dme.json b/tools/ticked_file_enforcement/schemas/beestation_dme.json new file mode 100644 index 0000000000000..5725279247936 --- /dev/null +++ b/tools/ticked_file_enforcement/schemas/beestation_dme.json @@ -0,0 +1,10 @@ +{ + "file": "beestation.dme", + "scannable_directory": "code/", + "subdirectories": true, + "excluded_files": [], + "forbidden_includes": [ + "code/modules/tgs/**/*.dm", + "code/modules/unit_tests/[!_]*.dm" + ] +} diff --git a/tools/ticked_file_enforcement/schemas/unit_tests.json b/tools/ticked_file_enforcement/schemas/unit_tests.json new file mode 100644 index 0000000000000..f5b7582096bfd --- /dev/null +++ b/tools/ticked_file_enforcement/schemas/unit_tests.json @@ -0,0 +1,9 @@ +{ + "file": "code/modules/unit_tests/_unit_tests.dm", + "scannable_directory": "code/modules/unit_tests/", + "subdirectories": false, + "excluded_files": [ + "find_reference_sanity.dm" + ], + "forbidden_includes": [] +} diff --git a/tools/ticked_file_enforcement/ticked_file_enforcement.py b/tools/ticked_file_enforcement/ticked_file_enforcement.py new file mode 100644 index 0000000000000..86c399c73554b --- /dev/null +++ b/tools/ticked_file_enforcement/ticked_file_enforcement.py @@ -0,0 +1,156 @@ +import codecs +import fnmatch +import functools +import glob +import json +import os +import sys + +# simple way to check if we're running on github actions, or on a local machine +on_github = os.getenv("GITHUB_ACTIONS") == "true" + +def green(text): + return "\033[32m" + str(text) + "\033[0m" + +def red(text): + return "\033[31m" + str(text) + "\033[0m" + +def blue(text): + return "\033[34m" + str(text) + "\033[0m" + +schema = json.load(sys.stdin) +file_reference = schema["file"] +file_reference_basename = os.path.basename(file_reference) +scannable_directory = schema["scannable_directory"] +subdirectories = schema["subdirectories"] +FORBIDDEN_INCLUDES = schema["forbidden_includes"] +excluded_files = schema["excluded_files"] + +def post_error(string): + print(red(f"Ticked File Enforcement [{file_reference}]: " + string)) + if on_github: + print(f"::error file={file_reference},line=1,title=Ticked File Enforcement::{string}") + +for excluded_file in excluded_files: + full_file_path = scannable_directory + excluded_file + if not os.path.isfile(full_file_path): + post_error(f"Excluded file {full_file_path} does not exist, please remove it!") + sys.exit(1) + +file_extensions = ("dm", "dmf") + +reading = False +lines = [] +total = 0 + +with open(file_reference, 'r') as file: + for line in file: + total += 1 + line = line.strip() + + if line == "// BEGIN_INCLUDE": + reading = True + continue + elif line == "// END_INCLUDE": + break + elif not reading: + continue + + lines.append(line) + +offset = total - len(lines) +print(blue(f"Ticked File Enforcement: {offset} lines were ignored in output for [{file_reference}].")) +fail_no_include = False + +scannable_files = [] +for file_extension in file_extensions: + compiled_directory = f"{scannable_directory}/**/*.{file_extension}" + scannable_files += glob.glob(compiled_directory, recursive=True) + +if len(scannable_files) == 0: + post_error(f"No files were found in {scannable_directory}. Ticked File Enforcement has failed!") + sys.exit(1) + +for code_file in scannable_files: + dm_path = "" + + if subdirectories is True: + dm_path = code_file.replace('/', '\\') + else: + dm_path = os.path.basename(code_file) + + included = f"#include \"{dm_path}\"" in lines + + forbid_include = False + for forbidable in FORBIDDEN_INCLUDES: + if not fnmatch.fnmatch(code_file, forbidable): + continue + + forbid_include = True + + if included: + post_error(f"{dm_path} should NOT be included.") + fail_no_include = True + + if forbid_include: + continue + + if not included: + if(dm_path == file_reference_basename): + continue + + if(dm_path in excluded_files): + continue + + post_error(f"Missing include for {dm_path}.") + fail_no_include = True + +if fail_no_include: + sys.exit(1) + +def compare_lines(a, b): + # Remove initial include as well as the final quotation mark + a = a[len("#include \""):-1].lower() + b = b[len("#include \""):-1].lower() + + split_by_period = a.split('.') + a_suffix = "" + if len(split_by_period) >= 2: + a_suffix = split_by_period[len(split_by_period) - 1] + split_by_period = b.split('.') + b_suffix = "" + if len(split_by_period) >= 2: + b_suffix = split_by_period[len(split_by_period) - 1] + + a_segments = a.split('\\') + b_segments = b.split('\\') + + for (a_segment, b_segment) in zip(a_segments, b_segments): + a_is_file = a_segment.endswith(file_extensions) + b_is_file = b_segment.endswith(file_extensions) + + # code\something.dm will ALWAYS come before code\directory\something.dm + if a_is_file and not b_is_file: + return -1 + + if b_is_file and not a_is_file: + return 1 + + # interface\something.dm will ALWAYS come after code\something.dm + if a_segment != b_segment: + # if we're at the end of a compare, then this is about the file name + # files with longer suffixes come after ones with shorter ones + if a_suffix != b_suffix: + return (a_suffix > b_suffix) - (a_suffix < b_suffix) + return (a_segment > b_segment) - (a_segment < b_segment) + + print(f"Two lines were exactly the same ({a} vs. {b})") + sys.exit(1) + +sorted_lines = sorted(lines, key = functools.cmp_to_key(compare_lines)) +for (index, line) in enumerate(lines): + if sorted_lines[index] != line: + post_error(f"The include at line {index + offset} is out of order ({line}, expected {sorted_lines[index]})") + sys.exit(1) + +print(green(f"Ticked File Enforcement: [{file_reference}] All includes (for {len(scannable_files)} scanned files) are in order!")) diff --git a/tools/trait_validity/check.py b/tools/trait_validity/check.py new file mode 100644 index 0000000000000..10da9f34a15c8 --- /dev/null +++ b/tools/trait_validity/check.py @@ -0,0 +1,88 @@ +import os +import re +import sys + +define_regex = re.compile(r"(\s+)?#define\s?([A-Z0-9_]+)\(?(.+)\)?") + +def green(text): + return "\033[32m" + str(text) + "\033[0m" + +def red(text): + return "\033[31m" + str(text) + "\033[0m" + +# simple way to check if we're running on github actions, or on a local machine +on_github = os.getenv("GITHUB_ACTIONS") == "true" + +defines_file = "code/__DEFINES/traits/declarations.dm" +globalvars_file = "code/_globalvars/traits/_traits.dm" + +how_to_fix_message = f"Please ensure that all traits in the {defines_file} file are added in the {globalvars_file} file." + +def post_error(define_name): + if on_github: + print(f"::error file={defines_file},title=Define Sanity::{define_name} is defined in {defines_file} but not added to {globalvars_file}!") + else: + print(red(f"- Failure: {define_name} is defined in {defines_file} but not added to {globalvars_file}!")) + +number_of_defines = 0 + +if not os.path.isfile(defines_file): + print(red(f"Could not find the defines file '{defines_file}'!")) + sys.exit(1) + +if not os.path.isfile(globalvars_file): + print(red(f"Could not find the globalvars file '{globalvars_file}'!")) + sys.exit(1) + +defines_to_search_for = [] +missing_defines = [] +scannable_lines = [] + +with open(defines_file, 'r') as file: + reading = False + + for line in file: + line = line.strip() + + if line == "// BEGIN TRAIT DEFINES": + reading = True + continue + elif line == "// END TRAIT DEFINES": + break + elif not reading: + continue + + scannable_lines.append(line) + +for potential_define in scannable_lines: + match = define_regex.match(potential_define) + if not match: + continue + + number_of_defines += 1 + defines_to_search_for.append(match.group(2)) + +if number_of_defines == 0: + print(red("No defines found! This is likely an error.")) + sys.exit(1) + +if number_of_defines <= 450: + print(red(f"Only found {number_of_defines} defines! Something has likely gone wrong as the number of global traits should not be this low.")) + sys.exit(1) + +with open(globalvars_file, "r") as file: + globalvars_file_contents = file.read() + for define_name in defines_to_search_for: + searchable_string = "\"" + define_name + "\" = " + define_name + if not re.search(searchable_string, globalvars_file_contents): + missing_defines.append(define_name) + +if len(missing_defines): + for missing_define in missing_defines: + post_error(missing_define) + + print(red(how_to_fix_message)) + sys.exit(1) + +else: + print(green(f"All traits were found in both files! (found {number_of_defines} defines)"))