* Fixes a closet harddel
PopulateContents is called in init which means that the closet is being
qdelled but it's contents are not being properly removed when the
prob(1) is called and it returns the qdel hint. This returns the qdel
hint BEFORE parents init is called to stop this from happening
* okay im just stupid
* moves it
* Update code/modules/unit_tests/closets.dm
Co-authored-by: ShizCalev <ShizCalev@users.noreply.github.com>
* Update code/modules/unit_tests/closets.dm
Co-authored-by: ShizCalev <ShizCalev@users.noreply.github.com>
* Adds a unit test for species changes keeping clothings
Adds a unit test that checks if people who species change into a Lizard keeps their non-digitigrade shoes or not, which will also affect Monkeys.
* Micro optimizes ssair's turf init, saving 2 seconds
Most of this is making existing operations do more legwork, or cheaper.
I did add cycle checking to ONLY init turf linking, which required
creating a new proc.
Did some horrible horrible things in said proc to save like 0.8 seconds.
I think it was worth it.
About The Pull Request
Avoids stringifying key unless its necessary. This was done redundantly twice, but I locked it to just the isnum path, as REF will always return a string, and the other path passes istext.
Use sortTim directly instead of sort_list. sort_list is just sortTim but it copies the list, so it's just wasted cost.
I still would like the bespoke element key option, as that's the only way to drastically cut down costs on things like item descriptions and decals, but this is good for the general use case, and makes it marginally less pressing.
I also want to test if we'd be better off inserting into the list in sorted order rather than sorting it all in the end, but I suspect not.
About The Pull Request
Closets now initialize their contents once in dump_contents(). This saves more than 1.6 seconds of init time (all /obj/structure/closet now initialize in 84ms).
Not sure what assumptions this will break, there's a lot of closets, so separate PR.
cl
del: You can no longer see maint spawners before the round starts (but your rounds start faster now :) )
/cl
About The Pull Request
Deletes /obj/shapeshift_holder, replaces it with /datum/status_effect/shapechange_mob
Refactors Heretic worm form into a shapeshift spell
Refactors Wabbajack, and associated code
Fixes#69117Fixes#65653Fixes#59127Fixes#52786
Why It's Good For The Game
/obj/shapeshift_holder was one of the worst remaining abuses of /obj direct subtypes, so I replaced it with a cool fancy datum.
This also decouples the shapeshifting behavior entirely from the shapeshifting spell. So we have support for shapeshifted mobs not sourced from a spell. Which is neat, we could technically swap Wabbajack to use this in the future.
Changelog
cl Melbert
fix: Wabbajacking a shapeshifted mob no longer runtimes horribly. When a shapeshifted mob is wabbajacked, they'll now be removed from their shapeshift and stunned.
fix: Transforming via a shapeshift should no longer rob you of your hearing / runechat awareness.
fix: Shapeshifting plays nicer with holoparasites.
fix: Being polymorphed from a xeno to a non-xeno correctly makes you a non-xeno
refactor: Refactored shapeshifting, the shapeshift holder is now a status effect instead of an object.
refactor: Heretic worm form is a shapeshift spell now, this might have some minor behavioral changes but should overall be the same.
refactor: Refactored Wabbajack (+ cursed pool). Overall a bit more clean / consistent behavior.
/cl
* Unit tests range suck
Ok so we currently rely on some undefined behavior in energy_ball code
Namely, the range() family will return turfs in least/greatest get_dist
This is VERY useful for optimizing the tesla, but it's also undefined,
and lummy could change it any day now.
So let's at least unit test it so if it breaks we can remove it
About The Pull Request
Fixes#69483#69432 broke reagent transfer.
image
As we can see above, we've gone from removing reagents 100% of the time, to removing reagents only when methods is truthy and thus they get added to r_to_send. methods is not always truthy. Infact, more often than not it's NULL.
As a result, common reagent transfer methods just broke, duplicating reagents.
Sometimes this has interesting consequences, like in reagent reactions: https://tgstation13.org/parsed-logs/terry/data/logs/2022/08/26/round-189263/game.txt
image
This is what my search bar looked like highlighting the 1000+ explosions.
image
This adds a unit test to make sure reagent transfer actually works, then fixes the bug by caching reagents to be removed and removing them in a batch later on.
Why It's Good For The Game
Infinitely looping explosions tend to be loud and obnoxious. This kills the player. This also kills the server.
Unit tests are cool because my test is an absolute unit and I'm in awe at the size of that lad.
Changelog
cl
fix: Fixes reagent transfer not properly emptying the source of reagents when transferring to a target.
/cl
* - Fixes storage mass transfer
- Brings some sanity to storage procs
- Implements a griddle feature that never was
* Uncomment this
* Right-click attack fix
* Scoop fix
* Smartfridges use silent
* Restores some lost checks
* Fixes storage implants
* Makes condiments their own subtype, fixes geese, prepares for merging
* Fixes geese checking drink type instead of edible foodtype to eat gross food.
* Renames foodtype var on drinks to drink_types to prevent above from happening again because it KEEPS HAPPENING. DRINKS AREN'T FOOD!
* Makes Condiments their own subtype of reagent_containers because they don't make any use of being a subtype of food, at all.
* Starts moving things from food to /food/drink subtype in preparation for merging /food/drink with /drink
* fully removes Food subtype
* /reagent_containers/drinks are now /reagent_containers/cup - This is so it's no longer confused with eachother.
* /food/drinks is now /reagent_containers/cup/drinks, so we can keep their special abilities.
* Fixes a LOT of errors with food, which are STILL checking the reagent_containers, despite ACTUAL food being refactored away from it a long time ago.
This doesn't compile yet, but I do want to make sure my progress is well tracked.
* remove copypaste code, changes soda cans
* Removes most copy paste code between the two drinks, moving most stuff to parent whenever needed.
* Made soda cans their own subtype since they didn't share anything with glass bottles anyways.
* Fixes more problems with food/drinks, especially with geese. Geese really were just broken this whole time and no one said a word...
* Removes a snowflake signal, now that both drink types share a common one.
* Adds everything to the .dme
Currently my goal is to get this all compiling, then remove isGlass var by making glass be all glass ones only.
* Moves all icons into a single drinks dmi
I'm not that great at icon stuff, hopefully I didn't forget/break anything.
* Turns juices into their own subtype
This allows us to let them check for type in molotov, to both get rid of a use of isGlass, and so non-glass non-cartons don't show up as 'carton'.
* fixes compile issues, adds updatepaths
* a better updatepaths
* updates the damn maps now
* properly names the updatepath
* how did that get there
* i suck at handling merge conflicts
* how am i this bad
* code improvement and soda fix
* more fixes
* Don't be a timer
Ports from old food bottles to trans the reagents, rather than add a timer to.
* Merge conflicts and fixes bottle smashing
* Bottle smashing is now consistently functional regardless of how much liquid they have in them, when before it would spill first, then smash on the second hit.
* runs updatepaths again
* Event driven table
* Operating computer fix + loosening of check
* Unit testing
* IT NEEDS TO BE FORCE MOVE YOU GOTTA CLIMB TABLES AAAAH
* Migrated patient to carbon instead of human
Has no real bearing on the experiments tbh
* DNAs can be null apparently
* Simplify replacement code
* Move comments
thanks to Vallat for pointing this out
whoops turns out most verbs havent been queued since may 11th because I made /datum/controller/subsystem/verb_manager have the SS_NO_INIT flag, without also removing a check in verb_manager/proc/can_queue_verb() that stops the verb callback from being queued if the subsystem isnt initialized yet. since subsystems with SS_NO_INIT obviously never have initialized set to TRUE, this always failed for every verb manager subsystem except for SSinput (because it doesnt have SS_NO_INIT).
also adds a debug var to force a subsystem to always queue incoming verbs if possible.
now the default verb management subsystem, and speech_controller will successfully queue verbs again. SSinput always queued verbs so that shouldnt change.
About The Pull Request
replaces a ton of log_game with user.log_message so the log is added to individual and global logs.
adds a few logs for individual LOG_VICTIM, LOG_ATTACK etc logging.
adds logging for bluespace launchpad's tele coords being changed.
took the word "has" out of log_combat, as it's extra and just lengthens the log.
Why It's Good For The Admins
It's extremely laggy to open game.txt so an alternative is individual game logs
Changelog
cl
admin: A lot of game logs will now also be in individual game logs, for convenience in log diving.
admin: Added logging for bluespace launchpad x and y offset changes, which go to individual game logs.
admin: Attack logs will now be slightly shorter, one useless word was removed.
/cl
* Fixes seed extractors not taking seeds from plant bags.
- When refactored, it accidently changed it from taking the seed "from its loc" to "from the extractor itself", which always failed
* Unit test
* Genericises it a bit
* Adds Merge Conflicts Markers to Create_And_Destroy Blacklist
Hey there,
My bad, I forgot this used. It was suggested to use an #IFNDEF UNIT_TESTS, but the sole purpose of this is to fail very, very loudly in any context. I don't want linters to somehow fail and have this be ignored by unit tests such that it doesn't show up during that part of CI. I want it to fail and yell at you and scream every time this motherfucker initializes, so let's not bug this fella for the purposes of this test.
* small comment update
* lets move it to the list
* Refactors mothweek check
Mothweek HAS SIDE EFFECTS
Checking it on a day that causes a week offset will currently cause that
week offset to persist
That's dumb.
Also it's not very expansive, only covers a slim set of possibilities.
Instead, lets build something to generate all passing days over a period
of time, maybe 3 months out of 2 years.
Then we'll crosscheck that against some predecided "ok" dates
If either list disagrees with each other, we'll fail. That way we can't
miss an edgecase. or have issues with side effects
I like this pattern.
Currently, storage works as a subtype of /datum/component, utilizing GetComponent() and signals to operate. While this is a pretty good idea in theory, the execution was pretty trash, and we end up with alot of GetComponent() snowflake code (something that shouldn't even need to be used frankly), and a heaping load of scattered procs that lead into one another, and procs that don't get utilized properly.
Instead, this PR adds atom_storage and proc/create_storage(. . .) to every atom, allowing for the possibility of storage on quite frankly anything. Not only does this entirely remove the need for signals, but it heavily squashes down the number of needed procs in total (removing snowflake signal procs that just lead to one another), reducing overall proc overhead and improving performance.
* destroy proc holder pt1
- change proc_holder/spell to action/cooldown/spell
- docs all the spell vars, renames some of them
- removes some useless vars
- start with pointed spells, as they're easy
* kill proc_holder pt2
- kill a buncha vars and replace it with flags
- convert a ton over
- general code improvements
* kill proc_holders pt3
- convert a good few more spells
- rename some signals
- handle statpanel
- better docs
* kiill proc_holder pt4:
- restructure the file system of action.dm, separating a good amount of item actions and miscellaneous garbage into files where they belong slightly better. Also splits off item actions, cooldown actions, innate actions, etc. into their own files, overlal making it much better to work with
- converts touch attacks to actions
- converts blood crawl, jaunt subtype
* kills proc_holder pt5
- clears up some icon issues so all the currently converted pages don't have errors
- shapeshift
- some more action cleanup
* kills proc_holder pt5.5:
- some documentation
- reworks feedback to prevent oversight with teleports and stuff
* kills proc_holder pt6:
- converted cult spells
- converted magic missile
- converted mime spells
- chipped away at the errors
- removed some vars which were too general, replaced them with more locally applicable vars. for example "range" which could mean "projectile range" or "aoe radius" or whatever - instead of having a broad net which everyone applies to in a confusing matter, instead lets each spell delegate on their own.
- merged magic/spell and magic/aoe, as the comment intended
- more unified behavior for spell levelling
* kill proc_holders pt 6.5:
- replacing a buncha old proc_holders that have been updated to reduce some errors. sub 900 baby
* kills proc_holder pt 6.75:
- minor fixes
* kills proc_holder pt7:
- cuts down on some errors
- refactors some wiz events
* kills proc_holder pt 7.5:
- malf ranged modules
- some minor errors
* kills proc_holder pt 7.75:
- mor eminor error handling, cleaning up changes
* kill proc_holder pt8:
- refactors spell book
- refactors spell implant
- some more minor error fixing
* kill proc_holder pt 8.5:
- scan ability
* Adds some robust documentation
* kill proc_holder pt9:
- converts some / most mutations over
* kill proc_holder pt10:
- sort out all the granters
- refactor them slightly
- fix some compile errors
* Some set-unset sanity - going to need to test removing Share()
* Removes transfer actions. It doesn't seem to do anything.
- Transfer_actions was called when current = new_character so locially speaking the early return in Grant() should cause it to NOOP. Test this in the future though
* Removes sharing from actions, docs actions better
* Some better documentation for spell and spell components
* Kills proc_holder pt11:
- Finally finishes ALL THE SPELLS IN THE SPELL FOLDER
- Fixes some more errors
* kills proc_holder pt11.5:
- minor error fixing and sanity
* Method of sharing actions. Can be improved in the future, needs testing
* Implements a way to update the stat panel entry for a spell. Also gets rid of VV stuff, as you can update the bigflags directly in VV now.
* Curse of madness bug I put in.
* kills proc_holder pt12:
- sub 500 errors!
- converts cytology mobs
- converts and refactors spiders slightly
- some minor fixing around the place as usual
* kill proc_holder pt13
- Finishes heretic spells
- Sub 300 errors!
- some touch refactoring to account for mansus grasp
* kills proc_holder pt14:
- revenant
- minor bugfixing for heretic stuff
* kills proc_holder pt14.5:
- some missed stuff for revenant + heretic
* kills proc_holder pt15:
- alien abilities
- more minor fixing
- sub 100 errors. The end is nigh
* kill proc_holder pt16? 17:
- Finishes cult spells
- sub 50 errors!
- refactors the way charge works
- renames / moves some signals
* kills proc_holder pt final:
- sdql spells
- no more errors!
* Bugfixes round 1
* Various bugfixing
- documentation done
- give spell works
- can cast spell gives feedback conditionally
- is available takes into account casting ability
* Some accidental reversions + fixes
* Unit tests
* Completely refactors jaunting
- All bloodcrawling is now handled on the action itself instead of across various living procs
- slaughter demons have their own blood crawls
- jaunting dummies don't have side effects on destroy() anymore
* Wizard spell logging and even more refactoring
ever see the tram take 10 milliseconds per movement to move 2100 objects? now you have
https://user-images.githubusercontent.com/15794172/166198184-8bab93bd-f584-4269-9ed1-6aee746f8f3c.mp4
About The Pull Request
fixes#66887
done for the code bounty posted by @MMMiracles to optimize the tram so that it can be sped up. the tram is now twice as fast, firing every tick instead of every 2 ticks. and is now around 10x cheaper to move. also adds support for multiz trams, as in trams that span multiple z levels.
the tram on master takes around 10-15 milliseconds per movement with nothing on it other than its starting contents. why is this? because the tram is the canary in the coal mines when it comes to movement code, which is normally expensive as fuck. the tram does way more work than it needs to, and even finds new ways to slow the game down. I'll walk you through a few of the dumber things the tram currently does and how i fixed them.
the tram, at absolute minimum, has to move 55 separate industrial_lift platforms once per movement. this means that the tram has to unregister its entered/exited signals 55 times when "the tram" as a singular object is only entering 5 new turfs and exiting 5 old turfs every movement, this means that each of the 55 platforms calculates their own destination turfs and checks their contents every movement. The biggest single optimization in this pr was that I made the tram into a single 5x11 multitile object and made it only do entering/exiting checks on the 5 new and 5 old turfs in each movement.
way too many of the default tram contents are expensive to move for something that has to move a lot. fun fact, did you know that the walls on the tram have opacity? do you know what opacity does for movables? it makes them recalculate static lighting every time they move. did you know that the tram, this entire time, was taking JUST as much time spamming SSlighting updates as it was spending time in SStramprocess? well it is! now it doesnt do that, the walls are transparent. also, every window and every grille on the tram had the atmos_sensitive element applied to them which then added connect_loc to them, causing them to update signals every movement. that is also dumb and i got rid of that with snowflake overrides. Now we must take care to not add things that sneakily register to Moved() or the moved signal to the roundstart tram, because that is dumb, and the relative utility of simulating objects that should normally shatter due to heat and conduct heat from the atmosphere is far less than the cost of moving them, for this one object.
all tram contents physically Entered() and Exited() their destination and old turfs every movement, even though because they are on a tram they literally do not interact with the turf, the tram does. also, any objects that use connect_loc or connect_loc behalf that are on the same point on the tram also interact with each other because of this. now all contents of the tram act as if theyre being abstract_move()'d to their destination so that (almost) nothing thats in the destination turf or the exit turf can react to the event of "something laying on the tram is moving over you". the rare things that DO need to know what is physically entering or exiting their turf regardless of whether theyre interacting with the ground can register to the abstract entered and exited signals which are now always sent.
many of the things hooked into Moved(), whether it be overrides of Moved() itself, or handlers for the moved signal, add up to a LOT of processing time. especially for humans. now ive gotten rid of a lot of it, mostly for the tram but also for normal movement. i made footsteps (a significant portion of human movement cost) not do any work if the human themselves didnt do the movement. i optimized has_gravity() a fair amount, and then realized that since everything on the tram isnt changing momentum, i didnt actually need to check gravity for the purposes of drifting (newtonian_move() was taking a significant portion of the cost of movement at some points along the development process). so now it simply doesnt call newtonian_move() for movements that dont represent a change in momentum (by default all movements do).
also i put effort into 1. better organizing tram/lift code so that most of it is inside of a dedicated modules folder instead of scattered around 5 generic folders and 2. moved a lot of behavior from lift platforms themselves into their lift_master_datum since ideally the platforms would just handle moving themselves, while any behavior involving the entire lift such as "move to destination" and "blow up" would be handled by the lift_master_datum.
also
https://user-images.githubusercontent.com/15794172/166220129-ff2ea344-442f-4e3e-94f0-ec58ab438563.mp4
multiz tram (this just adds the capability to map it like this, no tram does this)
Actual Performance Differences
to benchmark this, i added a world.Profile(PROFILER_START) and world.Profile(PROFILER_START) to the tram moving, so that it generates a profiler output of all tram movement without any unrelated procs being recorded (except for world.Profile() overhead). this made it a lot easier to quantify what was slowing down both the tram and movement in general. and i did 3 types of tests on both master and my branch.
also i should note that i sped up the "master" tram test to move once per tick as well, simply because the normal movement speed seems unbearably slow now. so all recorded videos are done at twice the speed of the real tram on master. this doesnt affect the main thing i was trying to measure: cost for each movement.
the first test was the base tram, containing only my player mob and the movables starting on the tram roundstart. on master, this takes around 13 milliseconds or so on my computer (which is pretty close to what it takes on the servers), on this branch, it takes between 0.9-1.3 milliseconds.
ALSO in these benchmarks youll see that tram/proc/travel() will vary significantly between the master and optimized branches. this is 100% because there are 55 times more platforms moving on master compared to the master branch, and thus 55x more calls to this proc. every test was recorded with the exact same amount of distance moved
here are the master and optimized benchmark text files:
master
master base tram.txt
https://user-images.githubusercontent.com/15794172/166210149-f118683d-6f6d-4dfb-b9e4-14f17b26aad8.mp4
also this shows the increased SSlighting usage resulting from the tram on master spamming updates, which doesnt happen on the optimized branch
optimized
optimization base tram.txt
https://user-images.githubusercontent.com/15794172/166206280-cd849aaa-ed3b-4e2f-b741-b8a5726091a9.mp4
the second test is meant to benchmark the best case scaling cost of moving objects, where nothing extra is registered to movement besides the bare minimum stuff on the /atom/movable level. Each of the open tiles of the tram had 1 bluespace rped filled with parts dumped onto it, to the point that the tram in total was moving 2100 objects. the vast majority of these objects did nothing special in movement so they serve as a good base case. only slightly off due to the rped's registering to movement.
on master, this test takes over 100 milliseconds per movement
master 2000 obj's.txt
https://user-images.githubusercontent.com/15794172/166210560-f4de620d-7dc6-4dbd-8b61-4a48149af707.mp4
when optimized, about 10 milliseconds per movement
https://user-images.githubusercontent.com/15794172/166208654-bc10086b-bbfc-49fa-9987-d7558109cc1d.mp4
optimization 2000 obj's.txt
the third test is 300 humans spawned onto the tram, meant to test all the shit added on to movement cost for humans/carbons. in retrospect this test is actually way too biased in favor of my optimizations since the humans are all in only 3 tiles, so all 100 humans on a tile are reacting to the other 99 humans movements, which wouldnt be as bad if they were distributed across 20 tiles like in the second test. so dont read into this one too hard.
on master, this test takes 200 milliseconds
master 300 catgirls.txt
when optimized, this takes about 13-14 milliseconds.
optimization 300 catgirls on ram ranch.txt
Why It's Good For The Game
the tram is literally 10x cheaper to move. and the code is better organized.
currently on master the tram is as fast as running speed, meaning it has no real relative utility compared to just running the tracks (except for the added safety of not having to risk being ran over by the tram). now the tram of which we have an entire map based around can be used to its full potential.
also, has some fixes to things on the tram reacting to movement. for example on master if you are standing on a tram tile that contains a banana and the TRAM moves, you will slip if the banana was in that spot before you (not if you were there first however). this is because the banana has no concept of relative movement, you and it are in the same reference frame but the banana, which failed highschool physics, believes you to have moved onto it and thus subjected you to the humiliation of an unjust slipping. now since tram contents that dont register to abstract entered/exited cannot know about other tram contents on the same tile during a movement, this cannot happen.
also, you no longer make footstep sounds when the tram moves you over a floor
TODO
mainly opened it now so i can create a stopping point and attend to my other now staling prs, we're at a state of functionality far enough to start testmerging it anyways.
add a better way for admins to be notified of the tram overloading the server if someone purposefully stuffs it with as much shit as they can, and for admins to clear said shit.
automatically slow down the tram if SStramprocess takes over like, 10 milliseconds complete. the tram still cant really check tick and yield without introducing logic holes, so making sure it doesnt take half of the tick every tick is important
go over my code to catch dumb shit i forgot about, there always is for these kinds of refactors because im very messy
remove the area based forced_gravity optimization its not worth figuring out why it doesnt work
fix the inevitable merge conflict with master lol
create an icon for the tram_tunnel area type i made so that objects on the tram dont have to enter and exit areas twice in a cross-station traversal
add an easy way to vv tram lethality for mobs/things being hit by it. its an easy target in another thing i already wanted to do: a reinforced concept of shared variables from any particular tram platform and the entire tram itself. admins should be able to slow down the tram by vv'ing one platform and have it apply to the entire tram for example.
Changelog
cl
balance: the tram is now twice as fast, pray it doesnt get any faster (it cant without raising world fps)
performance: the tram is now about 10 times cheaper to move for the server
add: mappers can now create trams with multiple z levels
code: industrial_lift's now have more of their behavior pertaining to "the entire lift" being handled by their lift_master_datum as opposed to belonging to a random platform on the lift.
/cl
At some point, someone did a find and replace over this file, and completely screwed up the signal for Knockdown().
This caused components that relied on it, like the Knockoff component, to work way less often.
This PR fixes that.
It also goes through and cleans up the Knockoff component. More consistent style guide stuff, minor improvements, better documentation.
It also unit tests it.
* Fuck you (refactors ur tails)
* Errors
* Wow. Pain.
* Fixes up probably everything
* finish up here
* Fixes hard del maybe
* original owner hard del
* garbage collection runtime
* suck my peen byond
* Mapped tails
* motherfucker.
* motherrfucker. again.
* Whooopppppsie
* yeah bad idea
* Turns out external organs literally just sat in nullspace forever if their parent was deleted, and didnt Remove() themselves, causing harddels.
* So anyways I repathed all organs
* Fixes
* really.
* unit test... test
* unit test-test but it passes linters this time because im a moh-ron
* I've lost track of what im doing at this point
* Hopefully fixes hard del?
* meh
* Update code/datums/dna.dm
* things n stuff
* repath from master pull
Implements the new midround roll changes.
1. Split midround rulesets into heavy/light impact
First, midround rulesets will be split into light/heavy impact categories. For example, sleeper agent and midround thief would be classified as light impact, while blob/ninja would be classified as heavy impact.
This split will then be used to spawn lighter impact antagonists earlier into the round, and higher impact antagonists later into the round.
Midrounds before 25 minutes = 100% light impact / 0% heavy impact
Midrounds from 25-60 minutes = Varying chances, increasing in favor of heavy impact after enough time
Midrounds from 60+ minutes = 0% light impact / 100% heavy impact
Low impact threat rulesets are guaranteed to roll if there is enough threat. High impact threat rulesets will carry the same % chance as they do now. If a heavy impact threat ruleset cannot be rolled, but there is enough threat, a light impact threat ruleset will roll in its place. In the future, this can potentially be changed into not spawning any ruleset if the station is deemed extremely hostile, or even be tweaked into spawning protagonists like ERTs.
Alongside this, rulesets should be able to determine their minimum time required. For example, nuclear assault has terrible numbers--it's very high cost, and very low weight, which are basically the only variables it can configure. In Dynamic 2022, it should be able to have low cost, still low weight (though maybe nto as much), while only being rollable after 70 minutes or so.
2. Midround antagonists now roll as intervals, determined by midround threat
In order to make sure 50 midround threat spawns more antagonists than 10 midround threat, the intervals at which midround threats are spawned will change from 15-45 minutes to increments determined by midround threat. Larger midround threat = more, close intervals, smaller midround threat = less, farther intervals.
Any threat not spent on midrounds will still be carried into latejoins, which as before will still consume midround threat, and are unchanged by this document.
Refactors the confusion status effect. Removes "confusion strength" and replaces it with duration, which is measured in seconds.
This also allows them to use the adjust_timed_status_effect procs instead of their own.
Fun fact! 2 years ago when confusion was refactored into status effects, all confusion effects in the game were halved in duration. They were changed to status effects, which tick every 1 second by default, from life, which tick every 2 seconds by default, without any values changing.