Files
Joshua Kidder 7a3ad79506 All camelCase (Brute|Burn|Fire|Tox|Oxy|Organ|Stamina)(Loss) procs now use snake_case. UNDERSCORES RULE! (#94111)
## 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!
/🆑
2025-11-27 15:50:23 -05:00

192 lines
8.7 KiB
Plaintext

///Stage two of the heart attack.
#define ATTACK_STAGE_TWO 100
///Stage three of the heart attack.
#define ATTACK_STAGE_THREE 70
///Stage four of the heart attack.
#define ATTACK_STAGE_FOUR 35
///If we reduce heart damage enough, it will recover on its own.
#define ATTACK_CURE_THRESHOLD 160
///What is the max oxyloss we're willing to deal, to prevent people from passing out early.
#define OXYLOSS_MAXIMUM 40
/datum/status_effect/heart_attack
id = "heart_attack"
remove_on_fullheal = TRUE
alert_type = null
///A timer that ticks down until the heart fully stops
var/time_until_stoppage = 150
///Does this show up on medhuds?
var/visible = FALSE
/datum/status_effect/heart_attack/on_apply()
var/mob/living/carbon/human/human_owner = owner
if(!istype(human_owner) || !human_owner.can_heartattack())
return FALSE
RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_organ_removed))
RegisterSignal(owner, COMSIG_LIVING_MINOR_SHOCK, PROC_REF(minor_shock))
RegisterSignal(owner, COMSIG_HEARTATTACK_DEFIB, PROC_REF(defib_shock))
RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(electrocuted))
return TRUE
/datum/status_effect/heart_attack/on_remove()
UnregisterSignal(owner, list(COMSIG_CARBON_LOSE_ORGAN, COMSIG_LIVING_MINOR_SHOCK, COMSIG_HEARTATTACK_DEFIB, COMSIG_LIVING_ELECTROCUTE_ACT))
/datum/status_effect/heart_attack/tick(seconds_between_ticks)
var/mob/living/carbon/human/human_owner = owner
if(!istype(human_owner) || !human_owner.can_heartattack())
qdel(src) //No heart? No effects.
return
if(time_until_stoppage > ATTACK_CURE_THRESHOLD)
owner.visible_message(span_nicegreen("[owner] relaxes [owner.p_their()] body and stops clutching at [owner.p_their()] chest!"), span_nicegreen("The pain in your chest has subsided. You're cured!"))
qdel(src)
return
var/oxyloss_sum = 0 //A sum of the oxyloss we will inflict by the end of this cycle.
if(time_until_stoppage > ATTACK_STAGE_THREE)
if(SPT_PROB(5, seconds_between_ticks))
owner.playsound_local(owner, 'sound/effects/singlebeat.ogg', 25, FALSE, use_reverb = FALSE)
owner.adjust_stamina_loss(5)
if(time_until_stoppage <= ATTACK_STAGE_TWO && time_until_stoppage > ATTACK_STAGE_THREE) //This coughing gets replaced with worse coughing, no need to stack it.
if(SPT_PROB(10, seconds_between_ticks))
owner.emote("cough")
owner.adjust_stamina_loss(10)
oxyloss_sum += 4
if(time_until_stoppage <= ATTACK_STAGE_THREE) //At this point, we start with chat messages and make it clear that something is very wrong.
if(!visible)
ADD_TRAIT(owner, TRAIT_DISEASELIKE_SEVERITY_HIGH, type)
owner.med_hud_set_status()
visible = TRUE //We do not reset this status until it's fully cured. Once it's been made apparent, there's no reason to hide it again until it is resolved. It will only confuse players.
if(SPT_PROB(15, seconds_between_ticks))
to_chat(owner, span_danger("You feel a sharp pain in your chest!"))
if(SPT_PROB(15, seconds_between_ticks))
human_owner.vomit(VOMIT_CATEGORY_DEFAULT, lost_nutrition = 95)
owner.emote("cough")
oxyloss_sum += 1
if(SPT_PROB(8, seconds_between_ticks))
to_chat(owner, span_danger("You feel very weak and dizzy..."))
owner.adjust_confusion_up_to(6 SECONDS, 10 SECONDS)
owner.adjust_stamina_loss(20)
owner.emote("cough")
oxyloss_sum += 8
if(time_until_stoppage <= ATTACK_STAGE_FOUR) //And now we compound it with even worse effects.
if(SPT_PROB(5, seconds_between_ticks))
to_chat(owner, span_userdanger("It feels like you're shutting down..."))
owner.adjust_dizzy_up_to(4 SECONDS, 10 SECONDS)
owner.adjust_eye_blur_up_to(4 SECONDS, 20 SECONDS)
owner.adjust_stamina_loss(20)
if(SPT_PROB(5, seconds_between_ticks))
owner.emote("cough")
if(SPT_PROB(5, seconds_between_ticks))
to_chat(owner, span_userdanger("You cough. Everything goes dark. You're going to die soon."))
owner.adjust_temp_blindness(10 SECONDS) //Are you panicking yet? You should be panicking by now.
else
to_chat(owner, span_userdanger("As you cough, your chest surges in pain and darkness closes in around your sight."))
owner.adjust_temp_blindness(2 SECONDS)
owner.adjust_eye_blur_up_to(4 SECONDS, 20 SECONDS)
oxyloss_sum += 8
owner.Paralyze(1 SECONDS)
oxyloss_sum += 3
if(owner.get_oxy_loss() < OXYLOSS_MAXIMUM) //A bad enough roll on the verge of passing out might still push you over into unconciousness for a few seconds...?
owner.adjust_oxy_loss(oxyloss_sum)
if(time_until_stoppage <= 0)
if(owner.stat == CONSCIOUS)
to_chat(owner, span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!"))
owner.adjust_eye_blur(20 SECONDS)
human_owner.set_heartattack(TRUE)
owner.apply_status_effect(/datum/status_effect/heart_desperation) // To give the victim a final chance to shock their heart before losing consciousness
var/flash_type = /atom/movable/screen/fullscreen/flash
if(owner.client?.prefs?.read_preference(/datum/preference/toggle/darkened_flash))
flash_type = /atom/movable/screen/fullscreen/flash/black
owner.overlay_fullscreen("flash", flash_type)
addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob/living, clear_fullscreen), "flash", 1 SECONDS), 1 SECONDS)
qdel(src)
return FALSE
if(time_until_stoppage <= ATTACK_STAGE_TWO)
if(time_until_stoppage <= ATTACK_STAGE_FOUR)
owner.playsound_local(owner, 'sound/effects/singlebeat.ogg', 100, FALSE, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
else
owner.playsound_local(owner, 'sound/effects/health/slowbeat.ogg', 25, FALSE, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
else
owner.stop_sound_channel(CHANNEL_HEARTBEAT)
time_until_stoppage--
/datum/status_effect/heart_attack/get_examine_text()
if(!time_until_stoppage <= ATTACK_STAGE_THREE)
return
var/mob/living/carbon/human/human_owner = owner
var/cannot_grasp = (human_owner.usable_hands <= 0 || owner.incapacitated & INCAPABLE_RESTRAINTS || HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED))
return span_warning("[owner.p_they()] looks to be doubling over" + "[cannot_grasp ? " in pain!" : ", clutching [owner.p_their()] chest in pain!"]")
/datum/status_effect/heart_attack/on_remove()
REMOVE_TRAIT(owner, TRAIT_DISEASELIKE_SEVERITY_HIGH, type)
owner.med_hud_set_status()
return ..()
///End the heart attack due to heart removal.
/datum/status_effect/heart_attack/proc/on_organ_removed(datum/source, obj/item/organ)
SIGNAL_HANDLER
if(istype(organ, /obj/item/organ/heart))
qdel(src)
///Slightly reduces your timer. Can cure you if you really really want.
/datum/status_effect/heart_attack/proc/minor_shock(datum/source)
SIGNAL_HANDLER
time_until_stoppage += 18 //Good for keeping yourself up. Won't be easy to get over the cure threshold by yourself. You're going to need security beating the crap out of you with stunbatons, but it'll work.
if(prob(50)) //Also good for crafty solos who want to stunbaton themselves back to health. Timing will be key.
to_chat(owner, span_nicegreen("Something about being shocked makes the pain in your chest ease up!"))
///Makes major progress towards curing the attack.
/datum/status_effect/heart_attack/proc/defib_shock(obj/item/shockpaddles/source)
SIGNAL_HANDLER
time_until_stoppage += 50 //Three shocks should save pretty much anyone.
owner.visible_message(span_nicegreen("[owner] seems to be relieved of their pain as they're shocked by the [source]!"), span_nicegreen("The [source] shocks your heart awake, and you feel the pain in your chest ease up!"))
///Slightly reduces your timer, just like the minor shock signal. Slightly more relief because these use cases are generally more dangerous.
/datum/status_effect/heart_attack/proc/electrocuted(datum/source, shock_damage)
SIGNAL_HANDLER
time_until_stoppage += (20 + shock_damage * 1.15)
if(prob(50))
to_chat(owner, span_nicegreen("Something about being electrocuted makes the pain in your chest ease up!"))
///Alternative to penthrite that keeps you up for a few seconds after having a heart attack. Gives a bit of time to call for help regardless of when/where you've collapsed.
/datum/status_effect/heart_desperation
id = "heart_desperation"
duration = (4 SECONDS)
alert_type = null
/// List of traits to add/remove from our subject when we are in their system
var/static/list/subject_traits = list(
TRAIT_STABLEHEART,
TRAIT_NOHARDCRIT,
TRAIT_NOSOFTCRIT,
TRAIT_NOCRITDAMAGE,
)
/datum/status_effect/heart_desperation/on_apply()
owner.add_traits(subject_traits, type)
owner.Knockdown(2 SECONDS, 2 SECONDS)
return TRUE
/datum/status_effect/heart_desperation/tick(seconds_between_ticks)
owner.Knockdown(2 SECONDS, 2 SECONDS)
owner.set_silence_if_lower(2 SECONDS)
/datum/status_effect/heart_desperation/on_remove()
owner.remove_traits(subject_traits, type)
#undef ATTACK_STAGE_TWO
#undef ATTACK_STAGE_THREE
#undef ATTACK_STAGE_FOUR
#undef ATTACK_CURE_THRESHOLD
#undef OXYLOSS_MAXIMUM