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

221 lines
7.5 KiB
Plaintext

/**
* Component allowing you to create a linked list of mobs.
* These mobs will follow each other and attack as one, as well as sharing damage taken.
*/
/datum/component/mob_chain
/// If true then damage we take is passed backwards along the line
var/pass_damage_back
/// If true then we will set our icon state based on line position
var/vary_icon_state
/// Mob in front of us in the chain
var/mob/living/front
/// Mob behind us in the chain
var/mob/living/back
/datum/component/mob_chain/Initialize(mob/living/front, pass_damage_back = TRUE, vary_icon_state = FALSE)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.front = front
src.pass_damage_back = pass_damage_back
src.vary_icon_state = vary_icon_state
if (!isnull(front))
SEND_SIGNAL(front, COMSIG_MOB_GAINED_CHAIN_TAIL, parent)
parent.AddComponent(/datum/component/leash, owner = front, distance = 1) // Handles catching up gracefully
var/mob/living/living_parent = parent
living_parent.set_glide_size(front.glide_size)
/datum/component/mob_chain/Destroy(force)
if (!isnull(front))
SEND_SIGNAL(front, COMSIG_MOB_LOST_CHAIN_TAIL, parent)
front = null
back = null
return ..()
/datum/component/mob_chain/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_GAINED_CHAIN_TAIL, PROC_REF(on_gained_tail))
RegisterSignal(parent, COMSIG_MOB_LOST_CHAIN_TAIL, PROC_REF(on_lost_tail))
RegisterSignal(parent, COMSIG_MOB_CHAIN_CONTRACT, PROC_REF(on_contracted))
RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_death))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_deletion))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(parent, COMSIG_ATOM_CAN_BE_PULLED, PROC_REF(on_pulled))
RegisterSignals(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_MOB_ATTACK_RANGED), PROC_REF(on_attack))
RegisterSignal(parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(on_glide_size_changed))
if (vary_icon_state)
RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON_STATE, PROC_REF(on_update_icon_state))
update_mob_appearance()
if (pass_damage_back)
RegisterSignals(parent, COMSIG_LIVING_ADJUST_STANDARD_DAMAGE_TYPES, PROC_REF(on_adjust_damage))
RegisterSignal(parent, COMSIG_LIVING_ADJUST_STAMINA_DAMAGE, PROC_REF(on_adjust_stamina))
RegisterSignal(parent, COMSIG_CARBON_LIMB_DAMAGED, PROC_REF(on_limb_damage))
var/datum/action/cooldown/worm_contract/shrink = new(parent)
shrink.Grant(parent)
/datum/component/mob_chain/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_CAN_BE_PULLED,
COMSIG_ATOM_UPDATE_ICON_STATE,
COMSIG_CARBON_LIMB_DAMAGED,
COMSIG_LIVING_ADJUST_BRUTE_DAMAGE,
COMSIG_LIVING_ADJUST_BURN_DAMAGE,
COMSIG_LIVING_DEATH,
COMSIG_LIVING_ADJUST_OXY_DAMAGE,
COMSIG_LIVING_ADJUST_STAMINA_DAMAGE,
COMSIG_LIVING_ADJUST_TOX_DAMAGE,
COMSIG_LIVING_UNARMED_ATTACK,
COMSIG_MOB_ATTACK_RANGED,
COMSIG_MOB_CHAIN_CONTRACT,
COMSIG_MOB_GAINED_CHAIN_TAIL,
COMSIG_MOB_LOST_CHAIN_TAIL,
COMSIG_MOVABLE_MOVED,
COMSIG_MOVABLE_UPDATE_GLIDE_SIZE,
COMSIG_QDELETING,
))
qdel(parent.GetComponent(/datum/component/leash))
var/mob/living/living_parent = parent
var/datum/action/cooldown/worm_contract/shrink = locate() in living_parent.actions
qdel(shrink)
/// Update how we look
/datum/component/mob_chain/proc/update_mob_appearance()
if (!vary_icon_state)
return
var/mob/living/body = parent
body.update_appearance(UPDATE_ICON_STATE)
/// Called when something sets us as ITS front
/datum/component/mob_chain/proc/on_gained_tail(mob/living/body, mob/living/tail)
SIGNAL_HANDLER
back = tail
update_mob_appearance()
/// Called when our tail loses its chain component
/datum/component/mob_chain/proc/on_lost_tail()
SIGNAL_HANDLER
back = null
update_mob_appearance()
/// Called when our tail gets pulled up to our body
/datum/component/mob_chain/proc/on_contracted(mob/living/shrinking)
SIGNAL_HANDLER
if (isnull(back))
return
back.forceMove(shrinking.loc)
var/datum/action/cooldown/worm_contract/shrink = locate() in back.actions
if (isnull(shrink))
return
INVOKE_ASYNC(shrink, TYPE_PROC_REF(/datum/action, Trigger))
/// If we die so does the guy behind us, then stop following the leader
/datum/component/mob_chain/proc/on_death()
SIGNAL_HANDLER
back?.death()
qdel(src)
/// If we get deleted so does the guy behind us
/datum/component/mob_chain/proc/on_deletion()
SIGNAL_HANDLER
QDEL_NULL(back)
front?.update_appearance(UPDATE_ICON)
/// Pull our tail behind us when we move
/datum/component/mob_chain/proc/on_moved(mob/living/mover, turf/old_loc)
SIGNAL_HANDLER
if(isnull(back) || back.loc == old_loc)
return
back.Move(old_loc)
/// Update our visuals based on if we have someone in front and behind
/datum/component/mob_chain/proc/on_update_icon_state(mob/living/our_mob)
SIGNAL_HANDLER
var/current_icon_state = our_mob.base_icon_state
if(isnull(front))
current_icon_state = "[current_icon_state]_start"
else if(isnull(back))
current_icon_state = "[current_icon_state]_end"
else
current_icon_state = "[current_icon_state]_mid"
our_mob.icon_state = current_icon_state
if (isanimal_or_basicmob(our_mob))
var/mob/living/basic/basic_parent = our_mob
basic_parent.icon_living = current_icon_state
/// Do not allow someone to be pulled out of the chain
/datum/component/mob_chain/proc/on_pulled(mob/living/our_mob)
SIGNAL_HANDLER
if (!isnull(front))
return COMSIG_ATOM_CANT_PULL
/// Tell our tail to attack too
/datum/component/mob_chain/proc/on_attack(mob/living/our_mob, atom/target)
SIGNAL_HANDLER
if (target == back || target == front)
return COMPONENT_CANCEL_ATTACK_CHAIN
if (isnull(back) || QDELETED(target))
return
INVOKE_ASYNC(back, TYPE_PROC_REF(/mob, ClickOn), target)
/// Maintain glide size backwards
/datum/component/mob_chain/proc/on_glide_size_changed(mob/living/our_mob, new_size)
SIGNAL_HANDLER
back?.set_glide_size(new_size)
/// On gain or lose stamina, adjust our tail too
/datum/component/mob_chain/proc/on_adjust_stamina(mob/living/our_mob, type, amount, forced)
SIGNAL_HANDLER
if (forced)
return
back?.adjust_stamina_loss(amount, forced = forced)
/// On damage or heal, affect our furthest segment
/datum/component/mob_chain/proc/on_adjust_damage(mob/living/our_mob, type, amount, forced)
SIGNAL_HANDLER
if (isnull(back) || forced)
return
switch (type)
if(BRUTE)
back.adjust_brute_loss(amount, forced = forced)
if(BURN)
back.adjust_fire_loss(amount, forced = forced)
if(TOX)
back.adjust_tox_loss(amount, forced = forced)
if(OXY) // If all segments are suffocating we pile damage backwards until our ass starts dying forwards
back.adjust_oxy_loss(amount, forced = forced)
return COMPONENT_IGNORE_CHANGE
/// Special handling for if damage is delegated to a mob's limbs instead of its overall damage
/datum/component/mob_chain/proc/on_limb_damage(mob/living/our_mob, limb, brute, burn)
SIGNAL_HANDLER
if (isnull(back))
return
if (brute != 0)
back.adjust_brute_loss(brute, updating_health = FALSE)
if (burn != 0)
back.adjust_fire_loss(burn, updating_health = FALSE)
if (brute != 0 || burn != 0)
back.updatehealth()
return COMPONENT_PREVENT_LIMB_DAMAGE
/**
* Shrink the chain of mobs into one tile.
*/
/datum/action/cooldown/worm_contract
name = "Force Contract"
desc = "Forces your body to contract onto a single tile."
background_icon_state = "bg_heretic"
overlay_icon_state = "bg_heretic_border"
button_icon = 'icons/mob/actions/actions_ecult.dmi'
button_icon_state = "worm_contract"
cooldown_time = 30 SECONDS
melee_cooldown_time = 0 SECONDS
/datum/action/cooldown/worm_contract/Activate(atom/target)
SEND_SIGNAL(owner, COMSIG_MOB_CHAIN_CONTRACT)
StartCooldown()