About The Pull Request This PR seeks to revamp heretic in it's almost entirety. Closes #58435 Closes #62114 Closes #63605 image Gameplay changes: The heretic no longer starts with a Codex Cicatrix or a Living Heart. Heretics now draw transmutation runes by using any writing tool while having Mansus Grasp active. The Mansus Grasp can be used to remove heretic runes. Draining influences can be done with "right click". While draining, people who examine you may get a message hinting that you're interacting with an influence. Drained influences can also be dispersed with anomaly neutralizers! The Codex Cicatrix is now a researchable item that lets you gain additional knowledge from influences. The Codex can still draw and remove runes, and does it faster. The Living Heart is now the heretic's heart. Literally. It's the heart in their chest. Their heart takes on the appearance of a living heart, and they can pulse it on demand to track their targets. This makes an audible noise. If your heart is lost (you're disemboweled or whatever), you need to do a ritual to regain it! Casting any heretic spell (besides Grasp) requires a Focus. A Heretic Focus is a neck item they can transmute. Heretic robes also function as a focus when toggled up. Ascending also disables the need for a focus, because of course. Heretics now gain 1 knowledge point passively every 20 minutes. Sacrificing has been revamped entirely. A heretic now gains four sacrifice targets on spawn. One random crewmember One member of their department One member of security One member of command (a "high value" sacrifice) You can sacrifice people who are in in soft-crit, hard-crit, or dead. Sacrificing someone will cuff them (if they are not), HEAL them, revive them, and set them unconscious. After a short time. they will be sent to a shadow realm. This shadow realm is themed to your heretic type. The shadow realm is a 2 minute long survival challenge where the sacrificee is subject to a constant barrage of shadowy hands. If they survive, they are teleported back to a random turf with no memory of how they got there. They'll also slur a TON to get the point across. If they die, their corpse is teleported back to a random turf on the station. No more multi-hearting! Your targets are your own. BUT adds a knowledge that allows heretics to reroll their sacrifice targets with a ritual. Each path now has a "Rituals of Knowledge". These are randomly generated rituals that may be difficult to complete but awards knowledge points in return. Ascending now has some requirements. To learn the ascension ritual, you need to complete all of the objective you are assigned. The ascension ritual now each have a varied requirement, instead of "needing 3 bodies" only. Other minor gameplay changes: Lots of balance tweaking. Buffed some summons. Buffed the Lord of the Night very slightly. Nerfed the Madness Mask. Put a limit on the amount of blade transmutations possible at once. 3 for flesh, 2 for other paths. Logs of BUG fixing. Rust Grasp is now based on right click for surfaces instead of combat mode. General grammar and flavor tweaks a ll around. Admin / code changes: Revamped the way heretics appear within the traitor panel. You can now easily see who they're targeting for sacrifice and what they have researched Also adds some helpful buttons to heretics, like giving them points! Refactored much, much of heretic code LIKE ALL OF HERETIC CODE WAS IN 4 FILES. Split up all the knowledge, spells, and items that belong to the heretic into their own files and folders. Not only that, but everything internally was still named "Eldritch Cultist" and similar. Almost every mention of "Eldritch Cultist" has been properly replaced by "Heretic". Much better reference handling all around. General code improvements over heretic stuff. Unit tests, because of course. Todo Sprites for the focus Look at adding 1-2 other objectives prior to ascension. Theft? Special rituals? ("Rust [x] tiles to be able to ascend") Why It's Good For The Game Okay but why? Heretics are not in a good place at the moment, this much is clear. They've been completely disabled on MRP for this reason. The reasoning is simple: A lot of murder. There's nothing inherently wrong with an antagonist heavy with murder, but the Heretic really missed the mark. Gib, gib, gib, then ascend so you can keep killing. In the background, the Heretic was FULL of flavorful spells, rituals, and "lore" stolen from Cultist Simulator that was unfortunate enough to be shackled to the heretic's gameplay loop. So, this revamp aims to amend that: Dial back the heretic's focus on mass murder and put more focus on the heretic's interesting flavor. Spooky maintenance rituals, knowledge seeking maniac. Sacrifice no longer outright kills / requires murder, meaning a heretic can progress without racking up a bodycount. Influence is gained passively over time, so they can spend influence on more interesting side paths. Side paths are required to progress to ascension, so they're encouraged to explore new things. Ultimately, while there still may be a little way to go, this PR seeks to take a good leap in starting it. Changelog cl Melbert add: Large scale heretic revamp! expansion: The Codex Cicatrix is no longer a roundstart heretic item. Research is handled through their antag info UI. Rune drawing is done by using a writing tool with Mansus Grasp active in your offhand. The actual Codex is an unlockable ritual item now. expansion: The Living Heart is no longer a roundstart heretic item - their actual heart now becomes their Living Heart, and it makes a sound when triggered. Losing your heart (being disemboweled) will require you to do a ritual to regain it. expansion: The Hereic Antag UI has been overhauled, and now hosts much of their mechanics as well as providing some helpful tips for newer players. expansion: Most heretic spells now require a focus to cast. All heretics can make a basic focus necklace, and some heretic equipment also functions as a focus. (Credit to Imaginos for the focus sprite!) expansion: Heretics now passively gain +1 influence every 20 minutes. expansion: Heretic sacrificing has been reworked. You can now sacrifice people who are in soft crit or weaker. Sacrificing someone heals them, cuffs them, and teleports them to the SHADOW REALM, where they must dodge a barrage of hands to survive. Survive long enough and you return without memory - die, and your body will be thrown back. expansion: Heretics now have a few new rituals, including the Ritual of Knowledge, a randomly generated ritual that awards knowledge points. expansion: Heretic ascension now has a few requirements - you must complete your objectives assigned to you prior to learning the final ritual, and all the final rituals have been changed a bit! qol: Using the Heretic's Mansus Grasp on surfaces (EX: Rust Grasp) now works on right-click, instead of combat mode. qol: Used heretic influences can now be removed with a Anomaly Neutralizers. balance: Some heretic rituals are now limited in the amount they can make. You can only have up to 2 heretic blades crafted at once (3 if you are Path of Flesh). balance: The Lord of the Night has been buffed to be a little scarier. Did you know the Lord of the Night can eat arms to regain body parts and heal? balance: Buffed some heretic summons - mostly their health pools. balance: Nerfed the heretic's Mask of Madness. It can no longer infinite stam-crit you. balance: Nerfed the heretic's ash mark. balance: Nerfed a bunch of on-hit-heretic-blade effects. Many effects only apply on mark detonation now: Void blade silence, flesh blade wounds, ash blade gasp cooldown refund. fix: Fixed quite a few bugs and unintended behaviors with heretic code. refactor: Refactored and improved much of Heretic code. Improved the file structure dramatically. admin: The heretic's traitor panel has been beefed up a bit. /cl
Unit Tests
What is unit testing?
Unit tests are automated code to verify that parts of the game work exactly as they should. For example, a test to make sure that the amputation surgery actually amputates the limb. These are ran every time a PR is made, and thus are very helpful for preventing bugs from cropping up in your code that would've otherwise gone unnoticed. For example, would you have thought to check that beach boys would still work the same after editing pizza? If you value your time, probably not.
On their most basic level, when UNIT_TESTS is defined, all subtypes of /datum/unit_test will have their Run proc executed. From here, if Fail is called at any point, then the tests will report as failed.
How do I write one?
- Find a relevant file.
All unit test related code is in code/modules/unit_tests. If you are adding a new test for a surgery, for example, then you'd open surgeries.dm. If a relevant file does not exist, simply create one in this folder, then #include it in _unit_tests.dm.
- Create the unit test.
To make a new unit test, you simply need to define a /datum/unit_test.
For example, let's suppose that we are creating a test to make sure a proc square correctly raises inputs to the power of two. We'd start with first:
/datum/unit_test/square/Run()
This defines our new unit test, /datum/unit_test/square. Inside this function, we're then going to run through whatever we want to check. Tests provide a few assertion functions to make this easy. For now, we're going to use TEST_ASSERT_EQUAL.
/datum/unit_test/square/Run()
TEST_ASSERT_EQUAL(square(3), 9, "square(3) did not return 9")
TEST_ASSERT_EQUAL(square(4), 16, "square(4) did not return 16")
As you can hopefully tell, we're simply checking if the output of square matches the output we are expecting. If the test fails, it'll report the error message given as well as whatever the actual output was.
- Run the unit test
Open code/_compile_options.dm and uncomment the following line.
//#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between
Then, run tgstation.dmb in Dream Daemon. Don't bother trying to connect, you won't need to. You'll be able to see the outputs of all the tests. You'll get to see which tests failed and for what reason. If they all pass, you're set!
How to think about tests
Unit tests exist to prevent bugs that would happen in a real game. Thus, they should attempt to emulate the game world wherever possible. For example, the quick swap sanity test emulates a real scenario of the bug it fixed occurring by creating a character and giving it real items. The unrecommended alternative would be to create special test-only items. This isn't a hard rule, the reagent method exposure tests create a test-only reagent for example, but do keep it in mind.
Unit tests should also be just that--testing units of code. For example, instead of having one massive test for reagents, there are instead several smaller tests for testing exposure, metabolization, etc.
The unit testing API
You can find more information about all of these from their respective doc comments, but for a brief overview:
/datum/unit_test - The base for all tests to be ran. Subtypes must override Run(). New() and Destroy() can be used for setup and teardown. To fail, use Fail(reason).
/datum/unit_test/proc/allocate(type, ...) - Allocates an instance of the provided type with the given arguments. Is automatically destroyed when the test is over. Commonly seen in the form of var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human).
TEST_ASSERT(assertion, reason) - Stops the unit test and fails if the assertion is not met. For example: TEST_ASSERT(powered(), "Machine is not powered").
TEST_ASSERT_EQUAL(a, b, message) - Same as TEST_ASSERT, but checks if a == b. If not, gives a helpful message showing what both a and b were. For example: TEST_ASSERT_EQUAL(2 + 2, 4, "The universe is falling apart before our eyes!").
TEST_ASSERT_NOTEQUAL(a, b, message) - Same as TEST_ASSERT_EQUAL, but reversed.
TEST_FOCUS(test_path) - Only run the test provided within the parameters. Useful for reducing noise. For example, if we only want to run our example square test, we can add TEST_FOCUS(/datum/unit_test/square). Should never be pushed in a pull request--you will be laughed at.
Final Notes
- Writing tests before you attempt to fix the bug can actually speed up development a lot! It means you don't have to go in game and folllow the same exact steps manually every time. This process is known as "TDD" (test driven development). Write the test first, make sure it fails, then start work on the fix/feature, and you'll know you're done when your tests pass. If you do try this, do make sure to confirm in a non-testing environment just to double check.
- Make sure that your tests don't accidentally call RNG functions like
prob. Since RNG is seeded during tests, you may not realize you have until someone else makes a PR and the tests fail! - Do your best not to change the behavior of non-testing code during tests. While it may sometimes be necessary in the case of situations such as the above, it is still a slippery slope that can lead to the code you're testing being too different from the production environment to be useful.