## About The Pull Request
On production, I sign up for rounds (and I know I pushed the button
because the auto-deadmin message comes up) but it doesn't inject me in
the game and fails without a warning. I have no idea why it's doing this
but I shifted some stuff around and added a few logs to hopefully stop
this strange consistent failure for me to start a shift.
I just cleaned up a lot of the code and turned the ready defines into
strings instead of a boolean (so I could log it more efficiently with
little overhead, as well as standardize it to a define without people
doing wacky shit everywhere). I don't think this will fix the issue but
it will definitely make some things more clear as to why I'm not joining
rounds without warning despite clicking ready.
## Why It's Good For The Game
Cleans the code up while trying to resolve a really puzzling bug I've
been having.
## Changelog
🆑
server: The ready/not ready states of new players are logged when a
shift starts.
/🆑
## About The Pull Request
On the tin, just adds a link to
https://github.com/tgstation-operations/server-config to the game so
people are a little bit more aware of it.
Here's how it looks:
<img width="1386" height="275" alt="image"
src="https://github.com/user-attachments/assets/63d2d284-6d5f-4f41-ba80-abc33699f7c5"
/>
## Why It's Good For The Game
More visibility to a public resource, this wasn't feasible before the
present situation. It won't show up here unless the config variable is
set.
## Changelog
🆑
config: If you publicly host a copy of your server's config, you may now
link to the public URL in the 'CONFIGURL' setting in the server config.
It will show to players in the Escape Menu's Resources Carousel.
/🆑
## About The Pull Request
Removes the RTV config entry, as RTV was removed in
https://github.com/tgstation/tgstation/pull/86788
## Why It's Good For The Game
Remove leftover useless config entry
## Changelog
🆑 LT3
admin: Removed ability to enable Rock The Vote because RTV is no longer
a thing
/🆑
## About The Pull Request
Better correspondence in higher-level logging files (e.g. TGS's
DreamDaemonLog Diagnostic), also helpful in instances where Round ID is
not known (like if someone gives you a random log and the round ID has
been forgotten), pretty good to have it logged if we know it in all the
important stuff like dd.log.
## Why It's Good For The Game
Higher standard of information tracking without having to do infinite
amounts of UTC Timezone maths and what-not as it's becoming more common
for me to see stuff and then have to pull all sorts of other data in
order to make sure what I'm looking at is accurate to what I want (and
still having that degree of uncertainty is unideal).
## Changelog
🆑
server: Round ID is now output to `log_world()`, so it'll show up stuff
like world.log, dd.log, etc.
/🆑
## About The Pull Request
This provides a work around for issue: #92706
Which prevents user from using uplink.
YES -> A new verb called "Reset UI Positions". It puts them all to the
0,0 location which is top left of screen.
YES, too! -> Holding ALT and clicking the background of the window will
allow you to move it.
## Why It's Good For The Game
This provides ADMINs two ways to help antags that get their uplink page
stuck.
## About The Pull Request
The "NT Shopping Network" app now checks for if you have "Department
Budget" access instead of command access for letting you place orders
and not requests. All heads, cargo techs and the warden have it shift
start but it can be given to anyone (same as EVA access) and heads of
staff can add or remove it at their access consoles.
Also makes warden start with the app to hint that they have access to
the budget.
## Why It's Good For The Game
The access itself is good because letting heads choose who has access to
the department budget is obviously good.
Cargo techs get it cuz they already have access to it by having access
to the request consoles in cargo.
Warden gets it by default because HOS is usually busy
killing/arresting/patrolling while warden is the one actually managing
brig and the armory. If HOS really hates that warden has access to the
budget they can just remove it on their console.
## About The Pull Request
Extends the part of the crafting unit test that ensures consistency
between the total mats of the components of a recipe (or rather, the
result of said recipe) and a generic instance of the same type as its
result, previously only implemented on food recipes.
## Why It's Good For The Game
This ensures a degree of consistency with the material composition of
various objects in the game. I couldn't do it in the original PR as that
one was too big already and it took months to get it merged, and have
the relative bugs fixed.
Currently a WIP as I slowly deal with the unit test reports.
## Changelog
🆑
refactor: Follow-up to the crafting/material refactor from months ago.
All objects crafted with stacks now inherit their mat composition (not
necessarily the effects and color) by default, while previously only a
few things like chair, sinks and toilets did. Report any object looking
or behaving weirdly as a result.
fix: The material composition of ammo boxes is no longer a 1/10 of what
it's supposed to be. It was a shitty hack to make it harder to recycle
empty ammo boxes. Instead, they lose materials as they're emptied now.
/🆑
## About The Pull Request
The end of round DB shutdown message has been posting everywhere for a
while now, but it was just put down as a quick debugging thing for
something on production. If we're going to keep it around, it should at
least look nicer instead of having weird maligned spacing rules and what
not.
## About The Pull Request
Macros that are strings do not need to be doing this.
## Why It's Good For The Game
Less improper things to copy paste existing in the codebase
## About The Pull Request
It's just a partial cleanup of
anti-[STYLE](https://github.com/tgstation/tgstation/blob/master/.github/guides/STYLE.md)
code from /tg/'s ancient history. I compiled & tested with my helpful
assistant and damage is still working.
<img width="1920" height="1040" alt="image"
src="https://github.com/user-attachments/assets/26dabc17-088f-4008-b299-3ff4c27142c3"
/>
I'll upload the .cs script I used to do it shortly.
## Why It's Good For The Game
Just minor code cleanup.
Script used is located at https://metek.tech/camelTo-Snake.7z
EDIT 11/23/25: Updated the script to use multithreading and sequential
scan so it works a hell of a lot faster
```
/*
//
Copyright 2025 Joshua 'Joan Metekillot' Kidder
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
//
*/
using System.Text.RegularExpressions;
class Program
{
static async Task Main(string[] args)
{
var readFile = new FileStreamOptions
{
Access = FileAccess.Read,
Share = FileShare.ReadWrite,
Options = FileOptions.Asynchronous | FileOptions.SequentialScan
};
FileStreamOptions writeFile = new FileStreamOptions
{
Share = FileShare.ReadWrite,
Access = FileAccess.ReadWrite,
Mode = FileMode.Truncate,
Options = FileOptions.Asynchronous
};
RegexOptions regexOptions = RegexOptions.Multiline | RegexOptions.Compiled;
Dictionary<string, int> changedProcs = new();
string regexPattern = @"(?<=\P{L})([a-z]+)([A-Z]{1,2}[a-z]+)*(Brute|Burn|Fire|Tox|Oxy|Organ|Stamina)(Loss)([A-Z]{1,2}[a-z]+)*";
Regex camelCaseProcRegex = new(regexPattern, regexOptions);
string snakeify(Match matchingRegex)
{
var vals =
matchingRegex.Groups.Cast<Group>().SelectMany(_ => _.Captures).Select(_ => _.Value).ToArray();
var newVal = string.Join("_", vals.Skip(1).ToArray()).ToLower();
string logString = $"{vals[0]} => {newVal}";
if (changedProcs.TryGetValue(logString, out int value))
{
changedProcs[logString] = value + 1;
}
else
{
changedProcs.Add(logString, 1);
}
return newVal;
}
var dmFiles = Directory.EnumerateFiles(".", "*.dm", SearchOption.AllDirectories).ToAsyncEnumerable<string>();
// uses default ParallelOptions
// https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions?view=net-10.0#main
await Parallel.ForEachAsync(dmFiles, async (filePath, UnusedCancellationToken) =>
{
var reader = new StreamReader(filePath, readFile);
string oldContent = await reader.ReadToEndAsync();
string newContent = camelCaseProcRegex.Replace(oldContent, new MatchEvaluator((Func<Match, string>)snakeify));
if (oldContent != newContent)
{
var writer = new StreamWriter(filePath, writeFile);
await writer.WriteAsync(newContent);
await writer.DisposeAsync();
}
reader.Dispose();
});
var logToList = changedProcs.Cast<KeyValuePair<string, int>>().ToList();
foreach (var pair in logToList)
{
Console.WriteLine($"{pair.Key}: {pair.Value} locations");
}
}
}
```
## Changelog
🆑 Bisar
code: All (Brute|Burn|Fire|Tox|Oxy|Organ|Stamina)(Loss) procs now use
snake_case, in-line with the STYLE guide. Underscores rule!
/🆑
## About The Pull Request
A while ago I had a thought that adding a certain voice filter to the
squid tongue would be a funny change. Goofball never got around to
testing the specific filter parameters I'm using, so I'm going "fuck it,
we ball" with the filter in its current iteration.
## Why It's Good For The Game
A thematically-fitting tts filter for a tongue that didn't but could
reasonably be given one.
## About The Pull Request
Fixes https://github.com/tgstation/tgstation/issues/90641
Fixes https://github.com/tgstation/tgstation/issues/88366
Eliminates worries over virtualspace currency being sent to real
accounts.
When I was looking into why there were no flags for bitrunning areas.
Then I saw this mess:
<img width="929" height="889" alt="Code_2we2QjDyFp"
src="https://github.com/user-attachments/assets/8a807bfe-b566-4057-a8ea-2b306325687d"
/>
Not having enough space / being too lazy to refactor this is a silly
reason to not include flags for something like these virtual areas where
it can be quite helpful. Fortunately I am not too lazy ~~in this
moment~~ so here we go:
It was fairly logical to move over some of these to a separate flag,
which I've called `area_flags_mapping` since they pertain to maploading
things and terrain generation mostly. `area_flags` stays reserved for
general properties and now has more room than it did before for you
people to fill it with.
In doing this it's also neatened up the code quit a bit, as UNIQUE_AREA
was kind of everywhere and now that it's implied by default less areas
need to have it defined (or explicitly un-defined).
<details> <summary> Working as intended </summary>
<img width="787" height="448" alt="dreamseeker_p0Qts36tG1"
src="https://github.com/user-attachments/assets/25056f34-8d43-4be2-a293-e53df7a7d1db"
/>
<img width="383" height="59" alt="dreamseeker_Ek7TXCcpbA"
src="https://github.com/user-attachments/assets/89622974-9467-4cdb-8345-d684f7c9004b"
/>
</details>
## Why It's Good For The Game
Fixes an exploit, improves the area flags situation slightly.
## Changelog
🆑
fix: you can no longer send money from virtualspace to a real account
code: adds a flag for virtual areas so they can easily be checked, as
well as an easy helper proc, 'is_area_virtual(your_area)'
/🆑
## About The Pull Request
Should fix https://github.com/tgstation/tgstation/issues/93897
This implements a restock blacklist for SSwardrobe. If an item type is
in the blacklist, then SSwardrobe will never try to restock that item,
instead just deleting them.
## Why It's Good For The Game
Fixes ever-expanding pile of modsuits in nullspace, and lets modsuits
actually be wardrobed properly
## Changelog
🆑
fix: fixes an where modsuits would accumulate in nullspace over the
course of a round
/🆑
## About The Pull Request
This pull request makes the HFR process at the same rate as other
atmospheric components. Because its temperature changes previously
didn't take `seconds_per_tick` into account, heat output and a few
things related to it had to be adjusted to compensate. This also fixes a
bug preventing the moderator from being able to leak its gases from
cracked parts.
## Why It's Good For The Game
The HFR not processing with the rest of atmospheric machinery can
occasionally cause odd behavior such as pressure spikes in the waste
output every 1 in 4 atmos ticks, which this PR corrects.
## Changelog
🆑
fix: Fixed cracked HFR parts not being able to leak moderator gases.
code: The HFR now processes at the same rate as other atmos machinery.
/🆑
## About The Pull Request
So, partial language understanding scaled the % chance to translate
something based on the ranking in the most common words list
But the ranking isn't real the list is in alphabetical order
So, adds a new list of the 1000 most common words sorted by *frequency*,
which I found on a random github page.
Is it scientifically found? I have no idea, but it looks good enough to
work.
This list doesn't share the same 1000 words as our existing one, so I
added all the differing words (amounted to ~400) to the original list.
So now partial language understanding correctly translates words based
on frequency.
## Changelog
🆑 Melbert
fix: Partial language understanding now correctly has a higher chance of
translating more common words.
qol: Aphasia got slightly more words to work with
/🆑
## About The Pull Request
renames Debug2 and kills Debug as its last use (atleast the one
described in its comment) was removed
added it to the check to print to print stack traces to chat in the
instance your debugging without a debugger attached for some reason (if
this var is enabled on live it already causes random to_world messages,
kinda fuck it we ball at that point)
## Why It's Good For The Game
The first one was ENTIRELY unused now
this name makes more sense
## Changelog
N/A
## About The Pull Request
first part with the 1 minute return is so you can have a area without an
ambience without insane runtimes
second part: In the course of making
https://github.com/DarkPack13/SecondCity/pull/246 I experienced some
weird bugs where it was playing the sound but failing to get the length.
This notifies you why the fuck its happening cause its otherwise really
confusing
## Why It's Good For The Game
I spent a while debugging it, still REALLY confused why the bug is
actually happening in the first place, its somehow referencing a file
that's both gone, and no longer being referenced anywhere in the repo.
Either way this shit annoyed me lol.
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->
## About The Pull Request
Fixes#93875
Removes an obsolete SS_TICKER flag from SSpriority_effects, which was
originally put there by Absolucy for a priority bump. The original issue
with status effects pausing for long periods of time was very likely
caused by the SS_BACKGROUND flag on both SSprocessing and
SSfast_process, which SSpriority_effects doesn't have.
The reason why this issue surfaced is because of #93694, which caused
the inconsistency between the real wait time of 0.1s and passed
seconds_per_tick of 0.2s to become apparent in the duration of status
effects. Previously, duration was based on world.time, which was
unaffected by this inconsistency, even though it existed before my PR as
well.
<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->
## Why It's Good For The Game
Fixing all stun times being halved for no raisin is pretty good for the
game.
It's also better for the SSpriority_effects subsystem to respect the
subsystem priority order. If we need it to be higher in the order, then
make it that way, instead of using SS_TICKER to bypass it entirely.
<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->
## Changelog
<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and its effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->
🆑
fix: High-priority status effects like stuns no longer process (and thus
expire) twice as quickly as they should.
/🆑
<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->
## About The Pull Request
Fixes all instances of numbers being used as assoc list keys in things
that aren't alists, either by turning them into alists or changing the
keys to something else. Also adds new macros to support creating global
alists, as a few global lists became alists. Most of these are pretty
simple and self-explanatory but
- The GLOB.huds one necessitated rewriting because code depended on it
being a non-assoc list, which it technically was because the defines it
used as keys were numbers so BYOND turned it into a regular list, most
of this was for loops through all the subtypes of
`/datum/atom_hud/data/diagnostic` of which there's only one, so I just
changed it to get that type directly by key
- NT Frontier used number indexes which it looped through for some
reason and also passed to TGUI, changed these to strings and adjusted
the TGUI to match, I tested this and it works fine
## Why It's Good For The Game
Makes the code compile, I couldn't test everything but I tried to check
all usages of affected vars to make sure they wouldn't break from being
switched to alists, a TM might be in order just to be sure nothing's
fucked
## Changelog
🆑
refactor: rewrote all cases of numbers being used as keys in non-alist
associative lists
/🆑
## About The Pull Request
When a camera update is triggered, it is instead added to a queue on a
background subsystem
An AI entering a camera chunk which is queued to update will force the
update immediately (bypassing the queue)
While the root problem of this is, ultimately, not addressed...
<img width="554" height="58"
alt="467828777-eff3f0e5-49d6-4997-b4d7-05eff6432155"
src="https://github.com/user-attachments/assets/c2d6a5f5-d958-463e-959f-116bd0dab475"
/>
...the change will ultimately prevent update spam from consuming all of
the server's resources - instead allocating updates to the backburner in
times of high server stress (or on multi-z maps)
## Changelog
🆑 Melbert
refactor: Refactored the way camera updates are handled to hopefully
reduce some lag. Report any oddities
/🆑
## About The Pull Request
Ok so this explanation is gonna suck a bit I'm sorry. I was sittin in
coderbus and @flleeppyy mentioned this problem where SSticker counted
down to roundstart significantly faster then it should.
I poked on tg, and saw the same behavior. I did further poking, and
realized that despite wait being 20ds, the actual delay between fires
was 15ds.
I'll spare you the rest of the story, it turned out that SSticker's
next_fire was set to 0 at the start instead of world.time, so when we
did the KEEP_TIMING thing of incrementing next_fire by wait, we were
pretty much always going to be trying to fire on the very next tick.
This was prevented by the existing sanity check which limits how fast
KEEP_TIMING subsystems are allowed to try and recover their tick locked
nature, reducing it to 15ds instead of 1ds delay.
The actual problem here was next_fire not being set properly, which is
normally handled by a block in the while(1) block that checks to see if
the run level has changed. Unfortunately if you have the starting run
level, this never happens.
This impacts EXACTLY SSticker, and nothing else. For now at least.
Solution is to write similar code in Loop's setup block, which brings
SSticker's roundstart timer back into parity with real life. This has
the nice side effect of achieving the randomized starting delays we want
for subsystems, to prevent clumping. See discussion on #71730 for more
on that.
I'm going straight off the dome on this one. Didn't wanna bug mto about
it if I feel comfortable and all.
Please note, this does mean the roundstart lobby time is slower now,
it's accurate but it's slower. Might need config changes idk.
I also added some code that checks to see if the subsystem we're
randomizing is a ticker or not, I'm kind of dubious on the "wait is
actually TICKS for ONLY TICKER" thing but I'm not comfortable enough to
change that yet.
## Why It's Good For The Game
When we say the round starts in 5 minutes, it should actually do that.
## Changelog
🆑
fix: Fixed a timing issue with SS_KEEP_TIMING subsystems, which impacts
exactly SSticker. TLDR is roundstart will now actually take the time it
says it does, instead of 75% of it.
config: The roundstart delay config is now accurate, so you may want to
decrease it to not overlengthen lobby time. IDK you decide.
/🆑
## About The Pull Request
If you have an outfit with a modsuit control, and are equipping it to a
dummy for previews, when you unequip it SSwardrobe tries to delete the
individual parts when stashing the mod control since they are in the
suit's `contents`.
The control is then stashed, and the parts all cause hard deletes after
a while since the suit itself is still holding a ref to all of them.
Since there is currently no good fast way of checking all the modsuit
parts outside of a nest of istypes I added an item flag for it.
## Why It's Good For The Game
Fixes an oversight, should not have any performance impact.
## Changelog
🆑
fix: fixes an oversight where the wardrobe system tries to delete
modsuit parts when stashing the modsuit
/🆑
## About The Pull Request
this converts the dependency on byondstorage, which is laggy (due to
being a large json file written to disk every 10 seconds), to using
indexeddb, like we did prior to 516. this is achieved by using an iframe
to give us a persistent origin, as the web is evil and has invented
same-origin policy
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy.
this also hosts the iframe page on the github pages site for a secondary
repository (see tgstation/byond-client-storage) so it works OOTB without
requiring server operators to setup the webroot cdn (which i don't
believe is configured on the tg servers at the moment)
however, if a server is using the webroot cdn, it will use that instead
of github pages
you could also host the iframe.html page on a separate host from your
cdn or github pages if you wanted to
if we can't access the configured cdn at all, it failovers to use
byondstorage anyway, if the internet stops working and you still want
your chat history to save, i guess
## Why It's Good For The Game
saving this enormous json file is laggy, and this solution would fix
https://github.com/tgstation/tgstation/issues/89988 and fix
https://github.com/tgstation/tgstation/issues/92035
i am open to other solutions, but this does seem to require the least
amount of external dependencies of posed solutions
## Changelog
🆑
fix: you should experience less stutters every 10 seconds
server: server operators can now configure an alternative storage domain
for clientside data storage, read the example configuration for more
/🆑
---------
Co-authored-by: harryob <55142896+harryob@users.noreply.github.com>
## About The Pull Request
What it says on the tin, the GMM Station Spotlight trait will not
generate a spotlight if there isn't more than 1 player on the station.
## Why It's Good For The Game
People have been running into the GMM Trait triggering during offline
testing, and functionally it makes sense to not apply the effect if
there's no-one around to potentially act on that information. If you're
alone, and the richest person on the station, that's functionally not
going to change any time soon.
## Changelog
🆑
qol: The GMM Spotlight station trait will not create a lightbeam if
there's not more than 1 player alive.
/🆑
---------
Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com>
## About The Pull Request
Completing a combat bitrunning domain grants 0.8 score bonus per player
that escaped without being hit
## Why It's Good For The Game
I figured it be a fun challenge to try to nohit these things, especially
the megafauna ones. For a bonus.
## Changelog
🆑 Melbert
add: Nohitting a combat bitrunning domain rewards a higher score.
/🆑
## About The Pull Request
Rather than having 100 separate list variables on `SSaccessories`, have
1 list for all feature keys that are associated with sprite accessories
This way you can get a feature by doing
`SSaccessories.feature_list[key]`, instead of necessitating
`SSaccessories.ears_list`, `SSaccessories.tail_list`, etc.
This lets us cut back on a lot of boilerplate in prefs, dna, and organs
## Why It's Good For The Game
We can see the benefit in this example: This is all the code for horn
DNA, bodypart overlay, and preference
```dm
/datum/dna_block/feature/accessory/horn
feature_key = FEATURE_HORNS
```
```dm
/datum/bodypart_overlay/mutant/horns
layers = EXTERNAL_ADJACENT
feature_key = FEATURE_HORNS
dyable = TRUE
/datum/bodypart_overlay/mutant/horns/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEHAIR)
```
```dm
/datum/preference/choiced/species_feature/lizard_horns
savefile_key = "feature_lizard_horns"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
main_feature_name = "Horns"
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/horns
/datum/preference/choiced/species_feature/lizard_horns/icon_for(value)
return generate_lizard_side_shot(get_accessory_for_value(value), "horns")
```
## Changelog
🆑 Melbert
refactor: Refactored species unique organs slightly, particularly how
they are set up at game start. Report any oddities, like invisible tails
or wings
/🆑
---------
Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
## About The Pull Request
If an event is not found in the list, it will be null, so the stack
trace when there is an invalid one in there will always return "Invalid
event attempting to be configured." AKA nothing useful to indicate which
event in the config is actually causing the failure.
Also stops stack trace from declaring 'invalid' config when they are
simply not loaded due to the current map.
It should be printing out [variable] instead.
Also removes a bunch of non existent events from the events.json and
fixes a few inconsistencies with the default weights.
## Why It's Good For The Game
Stack traces with useful information are good.
## Changelog
Nothing player-facing
## About The Pull Request
Fixes up some of the blaring problems with the woefully unloved
dullahans, making them at least playable in time for halloween. List of
individual fixes in the changelog, but notable code changes include that
visible/audible messages can (currently exists for dullahan heads and
holopad projections) now be proxied, so that detached cameras can
optionally relay TTS, emotes, and other messages to the proper client.
## Why It's Good For The Game
Its spooky month!
## Changelog
🆑
fix: Dullahans spawn with their intended organs
fix: Dullahans can hear/see emotes
fix: Dullahan heads properly have preferences applied to them
fix: Dullahan heads no longer appear eyeless
fix: Dullahans can hear things from their head regardless of if their
body is nearby
fix: Dullahans can hear TTS messages
fix: Dullahans have a TTS voice
qol: Dullahans have a head in the character preview, for the sake of
easier customization
/🆑
## About The Pull Request
discussed in #93439, a generic proc for getting a list of all types
minus abstract types.
applies this to most instances of a for loop that currently filters out
abstract types
it SHOULD be a nothing burger for performance, however I have not bench
marked the difference. (also testing, there is a total of 7 calls in
init to it)
## About The Pull Request
- [ ] I tested this pr
Adds "Midround Mass Traitors" and "Midround Mass Changelings" heavy
rulesets
Midround Mass Traitors will turn a number of the crew into sleeper
agents
Midround Mass Lings will spawn a number of changeling meteors
BY DEFAULT:
- Traitors will spawn 2-4 sleeper agents
- Lings will spawn 2-3 changeling meteors
- They won't run without >=2 candidates
- They have no config blacklist (so the HoP can roll mass midround
traitor, despite not being able to roll normal midround traitor. However
default blacklisted roles like security officers are still blocked)
- They're weighted impossible on tier 0, same weight as most midrounds
on tier 1, and fairly high on tiers 2 and 3
- They're repeatable but very unlikely to repeat
- Significantly higher min pop brackets than their solo counter part
## Why It's Good For The Game
Adds more options for lategame antags than big midrounds
## Changelog
🆑 Melbert
add: Two new heavy dynamic rulesets: "Midround Mass Traitors" and
"Midround Mass Changelings"
/🆑
## About The Pull Request
What it says on the tin. Cargo now has new math for market elasticity,
each parameter and what it does is explained below
**1. Refactor**
1. **k_elasticity**: This is the elasticity as before applied on all
exports except now this is a floating-point value that swings from
`0->1` instead of 10e-10 like is the current case. This in laymen terms
is the percentile of an items actual cost that is sold on cargo [0 means
you get no credits, 0.5 means you get 50% of the items cost, 1 means you
get 100% of the items cost]. Whenever an item is sold on cargo this
value decreases by an amount that is determined by the next variable
2. **k_hit_percentile(default 5%)**: This is the value by which an
export `k_elasticity` decreases whenever an export is successful for
every unit of an item sold. The real formulae by which the exports
elasticity decreases is dependent on the total amount sold and is as
follows
<pre>k_elasticity -= amount sold (1 for most cases except stacks) *
k_hit_percentile</pre>
So subsequent exports yield lesser profits cause the elasticity
decreases. Now the rate at which the elasticity recovers is as follows
3. **k_recovery_time(default 10 minutes)**: This is the time (minimum
unit should be seconds) it takes for the elasticity to rebound back to
100% after it has reached full 0 but recovery process starts immediately
if it decreases at any point. So if elasticity becomes say 50% it means
it would take 5 minutes to reach 100% again
**2. Some Balance changes**
1. Profits earned from exporting gas is linear per mole sold so the more
gas you put in the more profits you get HOWEVER the max number of
credits you can make per canister is 15000 cr
2. Selling fish yields higher prices because it's no longer subject to
the old elasticity formula
3. Selling 50 sheets of anything will decrease future sale price by 10%
and will take 8 minutes to rebound back to 100% if it reaches 0
**3. Improvements:**
- `SSProcessing` subsystem no longer processes more than 180+ export
datums from round start itself but now starts out empty. It instead
processes only those exports whose elasticity has been impacted and
later cancels it after elasticity has reached 100% so performance of
this subsystem has been drastically improved
- export datums now respect `abstract_type` meaning they won't be
created and can be used as a skeleton body for subtypes. So datums like
`datum/material` & `datum/material/market` are not created anymore but
only their subtypes are so we save processing power & memory
- Shaved of a lot of dead exports that went unused
## Changelog
🆑
balance: cargo exports now have different prices with applied elasticity
code: Improved performance of export code
qol: stock blocks can be recycled for materials & show up as stock
blocks in order console sold items
refactor: refactored cargo export code in whole. Report bugs on GitHub
/🆑
---------
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
## About The Pull Request
Heretic has received a complete overhaul. This PR touches nearly every
aspect of the antagonist. For readability's sake, not every change is
going to be listed in this pull request.
For the full list of changes please refer to the design doc:
https://hackmd.io/@BiST8PJVRjiwVPY86U3bLQ/B11HyChz1g.
Code by Me, @Xander3359 and @Arturlang
TGUI by @Arturlang
Sprites by OrcaCora and GregorDM
Writing bits by @necromanceranne
### Core changes
- Cross-pathing has been removed. Main knowledge spells are now
exclusive to their path (for the most part).
- For every main knowledge unlocked (save for the robes and the blade
upgrade), Heretics can choose one option from a draft of 3 random side
knowledges (this is a free point).
- Heretics can now purchase side knowledges from a new tab, the
"Knowledge Shop". Side-knowledges have been divided by tier (Stealth,
Defense, Summons, Combat and Main). Tiers are unlocked as you progress
toward your main path.
- Heretics now gain the grasp and mark upgrade immediately, but their
main knowledge choices cost twice as much (except for the first spell,
the robes and the blade upgrade).
- Path specific robes have been introduced! They come with their own set
of quirks.
- Each Path has received a passive ability. This passive is upgraded
when you first create your robes, and again when you complete the Ritual
of Knowledge.
- Paths have been rebalanced as a result of the removal of cross-path
progression. Cosmic and Moon paths have received soft reworks.
- Upon unlocking the path 2nd level or reaching a total of 8 points
worth of knowledge, Heretics will lose the ability to blade break (and
the limit on blades all together).
- Ascension now automatically calls the shuttle with no possibility of a
recall.
- Late join Heretic has been removed.
### New UI
<img width="750" height="635" alt="moon path ui"
src="https://github.com/user-attachments/assets/184ef783-5c9c-48a1-a2f7-4807ca93e990"
/>
### Knowledge shop
<img width="787" height="669" alt="Knowledge shop"
src="https://github.com/user-attachments/assets/3dc89b84-8c70-4d47-b612-54396e3ea6e7"
/>
### Quality of life //General balance changes
- Heretics will now gain X-ray vision for a few seconds when nearby an
eldritch essence (this effect has a cooldown).
- Ritual of knowledge now requires 1 uncommon item instead of 2. You may
now use a stunprod instead of a baton to complete the ritual. Beartraps
have been removed from the list of possible reagents.
- The maximum number of possible sacrifices required to ascend has been
reduced from 6 to 5 while the minimum has been upped to 4.
- Codex Cicatrix no longer requires a special pen to be made.
### Passive abilities
- Heretics now start with a passive ability. You can find what it does
on the path info tab after a path has been selected, and what they gain
when upgraded.
- Crafting your first set of Eldritch robes will bump your passive to
level 2.
- Unlocking the 2nd level will subsequently unlock your "Ritual Of
Knowledge"
- Completing the ritual of knowledge or ascending will net you the final
level.
### Path Specific Robes
- Armorer's Ritual is no longer a side knowledge. Each path will have
their own unique version of the ritual. This is placed after the 2nd
spell in the tree.
- Robes can no longer be destroyed by fire and acid, grant t4 flash
protection (Moth Heretics stay winning) and protection against basic
syringes, to bring them on par with other antagonist's armor sets.
- The recipe to craft the robes is now a set of armor/vest, a mask (any
mask will do now, not just gas masks), plus the unique reagent required
for the blades (Plasma for Cosmic, Trash For Rust, match for Ash and so
on)
- Wearing the robes as a non-heretic may yield some unfortunate
side-effects.
### Moon Path Rework
Moon path rework.
Moon Heretics gain immunity to brain traumas and slowly regenerate brain
health. Equipping the moon amulette channels its effects through the
moon blade; making it unblockable and cause sanity damage instead of
brute. Ring leader's Rise now summons an army of harmless clones that
explode when attacked; the explosion briefly stuns non-heretics and
cause sanity and brain damage to them. Moon blade can also now be used
when pacified and Moon spells are no longer blocked by regular anti
magic, only mind magic protection.
**Cosmic Path Rework**
Cosmic path has received the biggest batch of changes alongside Moon.
The path has been dead last in ascension and pickrate (less than 5%) for
almost 2 years. It did gain some popularity over the last few months,
reaching the highest ascension rate in the game (12%) while mantaining a
relatively low pickrate.
Cosmic sits in a weird spot, where pretty much every knowledge
surrounding the path is either mediocre or, in the case of the
ascension, dysfunctional. Yet it has maintained a smidge of relevancy
due to how quickly Cosmic heretics can capture and sacrifice targets
thanks to Star Touch.
As a result, the best course of action would be to rebalance the
entirety of the kit; granting the heretic more tools to manipulate space
and dictate the flow of a fight, while lessening their ability to end a
confrontation by instantly sleeping their opponents.
lastly The Star Gazer is now ghost controlled ; And they shoot lazers!
<img width="636" height="451" alt="gazer gag 3"
src="https://github.com/user-attachments/assets/601d6881-c042-4e42-8ce6-ac90cd27848b"
/>
## Why It's Good For The Game
### Ok...but why do we want this?
Again, if you want my full reasoning, please check my doc
https://hackmd.io/@BiST8PJVRjiwVPY86U3bLQ/B11HyChz1g.
To keep it short and concise; Heretic is too complex and unintuitive for
its own good. Too impenetrable for new players and too abusable for
experienced players. This can be chalked up to a lot of poor design
decisions. But ultimately, what I believe being the biggest contributor
to the current status of Heretic is the ability to move into different
paths, also known as "Cross-Pathing".
### Cross Pathing my beloathed.
Cross-pathing, while cool in theory, overcomplicates the antagonist and
overloads them with power. Players dealing with the heretic are
incapable of working out what a given heretic can do. This also leads to
late game heretics having 3 rows Worth of action buttons and virtually
no weakness.
Over the last year, I've often received the understandable but also kind
of unfair accusations of making Heretic too powerful without a clear aim
or purpose.
My goal with the paths I've reworked over the last year (Rust,Void and
Blade) wasn't necessarily to just make them stronger (although that was
also part of the goal, as they were paths that were underperforming),
but for them to have more interactions with the sandbox and to better
live up to the fantasy presented to the player.
If an harbringer of frost gets countered by a cup of coffee, we probably
messed something up.
Unfortunately, the current incarnation of Heretic doesn't really allow
for surgical balance changes to specific paths. Every time a knowledge
gets buffed, we make every path that can easily tap onto that knowledge
stronger by default. It doesn't take a genius to understand why this
system is ultimately unsustainable.
### Blade Breaking
I feel that after a heretic has reached the near peak of their power,
they no longer need the ability to instantly escape any encounter. Check
my doc for my full reasoning.
## Less versatile, more specialized paths.
By removing cross-pathing, we remove a huge maintainability burden from
the antagonist. Paths can now be designed around clearer strengths and
weaknesses. They become easier to balance and less of an headache to
understand for everyone.
It also means we can give paths some needed quality of life quirks
without having to worry how such a change might have a knock-on effect
for other paths.
Ash heretics can finally let loose without dying by their own flames.
Cosmic Heretic can go to space without having to carry a modsuit. Moon
Heretic can use their abilities without fear of one random trauma
ruining their day, and so on.
### What a horrible night to have a curse...., wait how do I curse
people again?
As of right now the heretic tree has quite a hefty amount of trinkets
that pretty much never see use.
Partly because the tree itself is a nightmare to navigate. And partly
because why would anyone set up an elaborate plan or scheme when they
can unleash 2 rows of spell in the span of bunch of seconds.
Heretics mostly gravitate towards powers that push them towards greater,
more potent combat strength. If it doesn't contribute to killing people
quicker, it isn't worth doing for most. And given the opportunity cost
associated for taking those powers, they will remain that way so long as
there are better choices to be poached.
The new draft system encourages Heretics to play more with the tools at
their disposal. If you want to go for a specific combo from the side
path options, you may now do so by tapping into the knowledge shop.
Yes, the shop does include a few knowledges from the other paths. But
these are limited to 1 per path, are very expensive and can only be
unlocked very late into the shift.
## Drip Of the Mansus
The iconic heretic robe is actually sequestered to a side path that is
most easily access by only two paths at a time. Since heretic paths are
being made to be much more specialized, the most obvious way in which
this can be showcased is through an easily
identifiable outfit.
By using the robes, we can both telegraph WHAT heretic you are looking
at, and just how much power they've accumulated and when it is
reasonable to take the kid gloves off and treat them as a genuine
threat. If a heretic is in their
robes, that heretic is now a significantly more prominent danger to the
station.
It also serves as a useful means for gating some of the more powerful
effects of a heretic's path behind the robes, AND enable options for
disarming them of that power should they be captured without making it
something endemic to their mob.
A major problem with heretics is a lack of certainty as to how powerful
they have become. A heretics robes is one of the milestones to help
players dealing with heretics identify that.
### Will this be 100% fair and balanced?
This is a massive overhaul to a pretty complex and bloated antagonist.
I've done my best to show the changes to several maintainers and other
members of the community for their feedback. But at some point we'll
have to see how this behave in the environment to get a feel if
something is over or undertuned. (that's my way of saying, yes this is
likely gonna require a testmerge or two).
What I will say is that I'm not trying to change the core identity of
Heretic.
Heretics should have the upperhand in single encounters early on, be
able to joust a small group of players after they unlock their final
spell, and end the round when they ascend. They're a progression
antagonist. They should retain their payoff as well as pose a danger as
they grow stronger.
But if more players feel like they are more reliably able to play the
antagonist in more varied and interesting ways, rather than the
antagonist largely existing as a measuring stick for 'robustness' due to
its elitist design philosophy, then the rework has been a success. There
should be something for
everyone in the antagonist, as is true for all of our antagonist roles.
## About The Pull Request
`/proc/flatten_list()` -> `/proc/assoc_to_values()`, also changes the
code doc to mirror the language of `assoc_to_keys()`'s code doc
## Why It's Good For The Game
Having a proc called `assoc_to_keys` that takes an associative list and
returns a list of the keys, and also having a proc that takes an
associative list and returns a list of the values, and the latter not
being called `assoc_to_values` is very funny
## Changelog
🆑
code: renamed flatten_list proc to assoc_to_values
/🆑
---------
Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
loads a smaller centcomm and map, this reduces init time to like 10
seconds.
## Why It's Good For The Game
for when you want to test something real quick and you don't need a full
environment